How to Make Python Toolbox Use It's Folder Location as The Working Directory?

273
9
Jump to solution
2 weeks ago
CarlVricella
New Contributor III

I'm creating a script tool inside of a python toolbox (.pyt) and having a frustrating time dealing with files relative to the .pyt file location. This is a very standard thing in python (or really any development project) to have ancillary files in same directory and to access them based on their relative path. For an unexplained reason ArcGIS Pro chooses to change the working directory to an entirely different location from where the .pyt actually is stored. Please tell me I don't have to hardcode a path to access some files...

Tags (3)
1 Solution

Accepted Solutions
Brian_Wilson
Occasional Contributor III

It looks like you should be able to use either arcpy.env.scriptWorkspace or arcpy.env.packageWorkspace, on my machine those both mapped to the folder that the pyt file is stored in, so like this

import os, sys
import arcpy
sys.path.append(arcpy.env.packageWorkspace)
from taxmap import BuildTaxMap

Both my pyt and script tool versions run with that code.

Brian_Wilson_0-1714780490001.png

 

View solution in original post

9 Replies
Brian_Wilson
Occasional Contributor III

Okay! I won't tell you that. Just put ALL YOUR CODE ALL IN ONE FILE and then paste it into a tiny box in ArcGIS Pro! What could go wrong? Ha ha ha ha. Who cares about version control? ANYWAY

I am working on this problem right now too. I've been fiddling with the differences between Python toolboxes and Script tools the last couple days.

Something that might help you (since I have no real answer for you yet) is to print out the entire environment from inside a tool so you can look at what environment settings you have. This should include BOTH the system environment and the arcpy environment.

Then you can decide if you want to try to force it to use an environment setting in your Python to make it find your modules.

import os
import sys
import arcpy
# HA HA HA YES THIS IS WHAT I DO RIGHT NOW BUT I HATE IT AND WANT TO FIX IT
sys.path.append("K:/ORMAP_CONVERSION/ORMAP_PRO")
from taxmap import BuildTaxMap

def show_environment():
    arcpy.AddMessage(f"Current directory: \"{os.getcwd()}\"")
    arcpy.AddMessage("os environment --------------")
    for i in sorted(os.environ):
        arcpy.AddMessage(f"{i}, {os.environ[i]}")

    arcpy.AddMessage("arcpy environment --------------")
    for i in sorted(arcpy.env):
        arcpy.AddMessage(f"{i}, {arcpy.env[i]}")
    return

# Then call that function, in a PYT you can call it from "execute"
# or from a script tool, from the "main" section

if __name__ == "__main__":
    # This code executes every time you run the tool
    # from the toolbox, not just from the debugger.
    show_environment()

 

I will try to post something more definitive later but now I have to go fix a printing problem in a web app. Grr.

0 Kudos
Brian_Wilson
Occasional Contributor III

It looks like you should be able to use either arcpy.env.scriptWorkspace or arcpy.env.packageWorkspace, on my machine those both mapped to the folder that the pyt file is stored in, so like this

import os, sys
import arcpy
sys.path.append(arcpy.env.packageWorkspace)
from taxmap import BuildTaxMap

Both my pyt and script tool versions run with that code.

Brian_Wilson_0-1714780490001.png

 

CarlVricella
New Contributor III

This is what I was looking for. ESRI needs to start thinking a little harder when they put together documentation, something like this definitely should be mentioned in the guide for creating tools in a python toolbox. I wrote this post when I was a pretty frustrated after spending an hour going through their docs trying to find this. 

0 Kudos
DonMorrison1
Occasional Contributor III

I have code that uses os.path.dirname(__file__) to find the directory of the current .py file, then access the files based on that. Would that work?

Luke_Pinner
MVP Regular Contributor

 

I usually use something like:

from pathlib import Path
tool_dir = Path(__file__).parent

# Or
import os.path
tool_dir = os.path.dirname(__file__)

@CarlVricella wrote:

... This is a very standard thing in python (or really any development project) to have ancillary files in same directory and to access them based on their relative path...


Yes, but... the working directory for a python module or script is never guaranteed to be the directory in which that module or script is located.  This is only going to be the case when you run a script or import a module that is located in the same directory as the current working directory.

 

BrennanSmith1
New Contributor III

Not sure exactly what you have set up, but I have each of the tools in my python toolbox as separate .py files, in a folder named toolboxscripts that is in the same directory as the .pyt file.  I load them in like this:

import sys
import os
import arcpy
from importlib import reload
sys.path.append(os.path.join(os.path.dirname(__file__), "toolboxscripts"))

#import the individual tool scripts, forcing a reload of any imported tools/modules
#otherwise you would have to restart Arc each time you edited an individual tool script
#this way you just have to right click -> Refresh the toolbox
import pyFilename
reload(pyFilename)
from pyFilename import classToolName

class Toolbox(object):
        # List of tool classes associated with this toolbox
        self.tools  =  [classToolName]

 

DonMorrison1
Occasional Contributor III

I do something very similar - minimizing the code logic in the .pyt file and reloading the imported files each time. I also set up the tools so they can be easily run from the command line/debugger. For me it lets me organize the code better and makes testing and debugging much easier.

Brian_Wilson
Occasional Contributor III

@DonMorrison1  I consider this essential, because it's so very easy to break a PYT file and it's almost impossible to debug them from a normal developer environment.

I put a __version__ number in each file so that I can see what actually loaded. Output looks like this, I use arcpy.AddMessage to report on each at the start of a script.

ormap_script_tool 0.6
taxmap 0.8
ormapnum 1.1
Exported to C:\Temp\pdflib\tp8_9_17CB.pdf

DavidSolari
Occasional Contributor III

The logical endpoint of this is the geoprocessing modules feature, which allow you to bundle your Python toolbox(es) as members of a standard Python package. There's a very big learning curve and you're forced to deal with Python's perpetually half-baked build systems but your reward is a fully managed, easy to read bundle of code that you can load into different environments, complete with proper dependency management. And the toolboxes are accessed just like system tools which is pretty slick. Just watch out for using them in web tools, that's a whole other can of worms.