Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

A common coding pattern used in AML was to run an AML (with parameters) inside another AML.

An application that I'm currently developing would benefit from being able to run a Python script (with parameters) within another Python script.

However, this does not seem to be at all straightforward.

Using ArcGIS 10, I'm experimenting with wrapping the "inner" Python script into an ArcGIS tool that has the parameters. I thought it would be a simple matter to have the "outer" Python script use arcpy.ImportToolbox to import the toolbox and then run the tool(s) within it. However, in testing so far all my attempts to run the "inner" tool from the "outer" script appear to simply skip the "inner" tool (no error is thrown).

Any thoughts on how to do this are welcome. In the meantime I'll try to create some simple test code to illustrate better what I am trying to describe.

Test code is now here ...

My testinner.py script is:

inputString = arcpy.GetParameterAsText(0)

newFC = "C:\\Temp\\test.gdb\\" + inputString
arcpy.Copy_management("C:\\Temp\\test.gdb\\test",newFC)

My testouter.py script is:

import arcpy

inputString1 = arcpy.GetParameterAsText(0)
inputString2 = arcpy.GetParameterAsText(1)

arcpy.ImportToolbox("C:\\Temp\\test.tbx")

arcpy.testinner_test(inputString1)

arcpy.testinner_test(inputString2)

For testinner.py its tool needs a single String parameter.

For testouter.py its tool needs two String parameters

The two tools are placed in a test.tbx.

The test.gdb just needs a single empty feature class called test.

Once you have the above assembled, running the testinner tool with a string like 'abc' passed in as its parameter should result in feature class 'test' being copied to one called 'abc' OK.

But when you try running the testouter tool with two strings like 'uvw' and 'xyz' as its parameters, the testinner tool within testouter.py seems to run OK once, but sends ArcMap 10 SP2 on Vista SP2 to a Serious Application Error when trying to use it the second time.

The same test using Windows XP SP3 and ArcGIS Desktop 10 SP2 also produces a Serious Application Error at the same point.

================================================================================

The execfile method described by Jason allowed me to rearrange my code to that below and provides a solution to my test problem:

import arcpy

inputString1 = arcpy.GetParameterAsText(0)
inputString2 = arcpy.GetParameterAsText(1)

arcpy.ImportToolbox("H:/Temp/test.tbx")

# Write second Python script to an ASCII file for first parameter & execute it
f = open("H:/Temp/string1.py","w")
f.write('newFC = "H:/Temp/test.gdb/' + inputString1 + '"' + "\n")
f.write('arcpy.Copy_management("H:/Temp/test.gdb/test"' + ',newFC)')
f.close()
execfile("H:/Temp/string1.py")

# Write third Python script to an ASCII file for second parameter & execute it
f = open("H:/Temp/string2.py","w")
f.write('newFC = "H:/Temp/test.gdb/' + inputString2 + '"' + "\n")
f.write('arcpy.Copy_management("H:/Temp/test.gdb/test"' + ',newFC)')
f.close()
execfile("H:/Temp/string2.py")

However, this may prove cumbersome when applied to non-test script(s) which are much longer so I used blah238's generous work that endorsed Dan's approach (Jason also endorsed Dan's approach I may add) and came up with the following final (test) code that does exactly what I need.

================================================================================

# CopyFeaturesTool.py

import CopyFeaturesUtility
import arcpy
outputFCName = arcpy.GetParameterAsText(0)
outputFCName2 = arcpy.GetParameterAsText(1)

CopyFeaturesUtility.copyFeaturesToTempGDB("C:\\Temp\\test.gdb\\test", outputFCName)
CopyFeaturesUtility.copyFeaturesToTempGDB("C:\\Temp\\test.gdb\\test", outputFCName2)

and

# CopyFeaturesUtility.py

import arcpy
import os

def copyFeaturesToTempGDB(inputFeatures, outputName):
    """Copies the input features to a temporary file geodatabase.
    inputFeatures: The input feature class or layer.
    outputName: The name to give the output feature class."""

    tempGDB = r"C:\Temp\test.gdb"
    newFC = os.path.join(tempGDB, outputName)
    arcpy.env.overwriteOutput = True
    arcpy.Copy_management(inputFeatures, newFC)

if __name__ == '__main__':
    inputFC = r"C:\Temp\test.gdb\test"
    outputFCName = arcpy.GetParameterAsText(0)
    copyFeaturesToTempGDB(inputFC, outputFCName)

Many thanks to Dan, Jason and blah238 for helping me.

share|improve this question
1  
Going with @Dan's answer on this... don't think of .py files just as "scripts", think of them as modules you can reuse and recycle by importing the functions and classes you need from those modules. Abstract away those nested GP parameters by using one script to read in one set of parameters and then call functions in your other modules as needed. Use the if name__=='_main' trick to make your modules both importable and still useable standalone. –  blah238 Jun 23 '11 at 5:07
    
I have Dan's example working to output: C:\Temp\Main_program.py ('sum some numbers: ', 55) ('sum of squares: ', 385) ('hello from 8: ', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) but am struggling to adapt it to an ArcPy example like I gave above. Any further help on what an ArcPy example would look like would be greatly appreciated. –  PolyGeo Jun 23 '11 at 11:52
    
See the answer I added -- should help explain things in the context of your example better. –  blah238 Jun 23 '11 at 17:34
    
I just came across an excellent blog post from Jason Pardy that provides an ArcPy template incorporating the coding pattern for Python modules at blogs.esri.com/Dev/blogs/geoprocessing/archive/2011/07/21/… –  PolyGeo Aug 4 '11 at 23:00
    
this link has since moved, and I believe it rests here now: blogs.esri.com/esri/arcgis/2011/08/04/pythontemplate –  gotanuki Dec 7 '12 at 16:14

3 Answers 3

up vote 9 down vote accepted

Here is your test example modified to import a "utility" module within the main script and call a function using the parameters read in by the script tool:


CopyFeaturesTool.py - Script tool that reads in parameters and calls a function in another module

import CopyFeaturesUtility
import arcpy

inputFC = arcpy.GetParameterAsText(0)
outputFCName = arcpy.GetParameterAsText(1)
CopyFeaturesUtility.copyFeaturesToTempGDB(inputFC, outputFCName)

CopyFeaturesUtility.py - Module that has a single function copyFeaturesToTempGDB. Can either be imported or run standalone. If run standalone the code under if __name__ == '__main__' is run.

import arcpy
import os

def copyFeaturesToTempGDB(inputFeatures, outputName):
    """Copies the input features to a temporary file geodatabase.
    inputFeatures: The input feature class or layer.
    outputName: The name to give the output feature class."""

    tempGDB = r"c:\temp\test.gdb"
    newFC = os.path.join(tempGDB, outputName)
    arcpy.env.overwriteOutput = True
    arcpy.CopyFeatures_management(inputFeatures, newFC)

if __name__ == '__main__':
    inputFC = r"c:\temp\test.gdb\test"
    outputFCName = "testCopy"
    copyFeaturesToTempGDB(inputFC, outputFCName)

I think you'll find this modular approach to be much more efficient and logical once you've gotten used to it. The Modules section in the standard Python tutorial is also a good resource for understanding how importing works.

For more arcpy-specific examples take a look at the built-in scripts in your C:\Program Files\ArcGIS\Desktop10.0\ArcToolbox\Scripts folder.

share|improve this answer
    
Duplicate of Dan's post. –  Jason Scheirer Jun 23 '11 at 19:33
    
I agree that Dan's post pretty much covered it, but the OP asked for an arcpy example in the comments, so that is what this is for. Not a duplicate, just a similar approach adapted to his specific test example. –  blah238 Jun 23 '11 at 19:50
    
A generality is probably the best way to facilitate multiple platforms, however, each person has there own learning style. –  Dan Patterson Jun 24 '11 at 1:57

You can accomplish this by importing a module (ie script) into your main script and calling its functions. A simple demo is contained in the accompanying two scripts.

    '''
Main_program.py

demonstrates how to import and call functions from another module
'''
import sys
import CallingFunctions

a_list = [1,2,3,4,5,6,7,8,9,10]
print sys.argv[0]
print CallingFunctions.func1(a_list)
print CallingFunctions.func5(a_list)
print CallingFunctions.func8(a_list)

for the main program and for the functions that are getting called

'''
Callingfunctions.py

imported into another program giving it access to the functions
'''

def func1(inputs=None):
  x = sum(inputs)
  return "sum some numbers: ", x
'''
more functions
'''
def func5(inputs=None):
  x_sq = 0
  for x in inputs:
    x_sq += x**2
  return "sum of squares: ", x_sq
'''
more functions
'''
def func8(inputs=None):
  return "hello from 8: ", inputs

'''
more functions
'''
if __name__ == "__main__":
  a_list = [1,2,3,4,5,6,7,8,9,10]
  inputs = "test inputs"
  a_dict = {1:[func1([1,2,3]) ],
            5:[func5([1,2,3])],
            8:[func8("inputs to 8")]}
  needed = [1,5,8]
  for akey in needed:
    if akey in a_list:
      action = a_dict[akey]
      print "\naction: ", action

you just have to ensure that the main module and the child module are in the same folder. You can pass parameters to the child module easily and if the child module needs access to arcpy (assuming you are using version 10 of arcmap) simply pass a reference to it.

share|improve this answer
    
Thanks for responding Dan. I'll try setting up your test tomorrow and, assuming that works, will see if I can adapt its key elements into my test code (which I've now included in the edited question). –  PolyGeo Jun 22 '11 at 12:18

Importing and running a function is the cleaner way to do it, but for the sake of completeness, there is also the execfile built-in function (documentation) which will let you run an arbitrary file in the current context.

share|improve this answer
    
Thanks Jason. I'll try this too. It's actually a bit closer to how I was trying to do it yesterday by writing values to an ASCII file and then having the inner script run via import to read them. Unfortunately, that only allowed the inner script to be run once within the outer. I'll try both your and Dan's methods to see what works best for me. –  PolyGeo Jun 22 '11 at 22:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.