ArcGIS Python Tool - Importing Custom Script Into ToolValidator Class

The way I do this is, after starting ArcGIS or ArcCatalog, first run a dummy tool ("Run this once") calling a dummy.py script. After doing that you can import python scripts in the validator by using sys.argv[0]. This will point to the folder where the first script was located. Thereafter you can import the needed script in de Validator Class.

The dummy.py script called by "Run this once" tool:

import arcgisscripting, sys, os
gp = arcgisscripting.create(9.3)

# set up paths to Toolshare, scripts en Tooldata
scriptPath = sys.path[0]  
toolSharePath = os.path.dirname(scriptPath)
toolDataPath = toolSharePath + os.sep + "ToolData"
gp.addmessage("scriptPath: " + scriptPath)
gp.addmessage("toolSharePath: " + toolSharePath)
gp.addmessage("toolDataPath: " + toolDataPath)

# Use this to read properties, VERY handy!!
import ConfigParser
config = ConfigParser.SafeConfigParser()
config.readfp(open(scriptPath + os.sep + 'properties.ini'))
activeOTAP = config.get('DEFAULT', 'activeOTAP')
activeprojectspace = config.get('DEFAULT', 'activeprojectspace')
activeproject = config.get('DEFAULT', 'activeproject')
activesdeconnection = config.get('DEFAULT', 'activesdeconnection')

Sorry, can't get the formatting right Regards, Maarten Tromp


Finally cracked this horrible bug! For example when you try to apply changes to import a relative module or package, you might see the following error:

enter image description here

Option 1:
For the developer only, add the full path to the module to the PYTHONPATH. You will need to restart ArcMap/ArcCatalog before it takes effect. Use the code below to import the module from a relative path (for deployment). Don't worry, the end user does not need any additions to their PYTHONPATH variable, it will work!

Option 2:
Add an additional line into the code below to append the hard-coded path, for example: sys.path.append(r"c:\temp\test\scripts")
When you are ready to deploy, you have an extraneous directory, but it doesn't matter, all should work on the enduser's computer because the first path you added was the relative directory (our goal was to just get past the failure dialog).

Code common to both options:

import os
import sys

tbxPath = __file__.split("#")[0]
srcDirName = os.path.basename(tbxPath).rstrip(".tbx").split("__")[0] + ".src" 
tbxParentDirPath =  os.path.dirname(tbxPath)
srcDirPath = os.path.join(tbxParentDirPath, srcDirName)

sys.path.append(srcDirPath)
# sys.path.append(r"c:\temp\test\scripts")  #option2

from esdlepy.metrics.validation.LandCoverProportions import ToolValidator

Update

Farewell evil cutting and pasting! I updated the code sample so the ToolValidator class is imported from the library. I cut and paste only once when the tool parameters are first set. I store this code snippit in the docstring of the ToolValidator being imported.

In this example, the source directory name is based of the tbx name. This approach avoids collisions if you have two toolboxes with different source directories. The standard I used for source folder naming is as follows:
TOOLBOXNAME__anything.tbx -> TOOLBOXNAME.src

Why the "__anything"? Since binary files can't be merged in our DVCS, we can assign tools to individuals and not worry about losing changes. When the tool is finalized, it is cut and pasted into the master.

I also needed to access files in the source folder to populate a dropdown, use this method to get the path to the toolbox from within your imported module:

import __main__
tbxPath = __main__.__file__.split("#")[0]

Putting the imports at the top of the validation module, outside of the ToolValidator class seems to work fine for me -- I am on 10.0 SP2. However I am not doing anything with the imported module anywhere but in updateParameters.

import os
import sys
scriptDir = os.path.join(os.path.dirname(__file__.split("#")[0]), "Scripts") 
sys.path.append(scriptDir)
from someModuleInScriptDir import someFunction

class ToolValidator:
    ...