17

When using a python toolbox (.pyt) in ArcMap, I'll typically follow a pattern where the .pyt file itself is simply a wrapper to collect input arguments and define the tools themselves. Supporting code is kept in separate unit-testable modules.

Example:

import supporting_module

class MyTool(object):

    ...

    def execute(self, parameters, messages):
        """The source code of the tool."""
        some_input = parameters[0].valueAsText
        some_output = parameters[1].valueAsText

        supporting_module.do_something(some_input, some_output)
        arcpy.SetParameter(2, some_output)

This works out really well but I've run into one frustrating issue during development. Right-click -> Refresh on the .pyt in ArcMap only refreshes the .pyt file's code. It does not refresh the imported modules, so I have to close and re-open ArcMap whenever I change something there. Fortunately, since I'm testing the code independently, I don't have to do this a ton, but it's still a major hassle. Is there any way around this? Somewhat related - is there any way to fully refresh the Python console (i have a custom site-package that I must also close/reopen ArcMap to pull in changes from as well)?

I'm using ArcMap 10.2.1.

0

4 Answers 4

17

I found this possibility, https://stackoverflow.com/questions/1517038/python-refresh-reload

The one caveat is that if you have any variables assigned to the module, they will need to be assigned again.

But as you have it written above, you could do this:

import supporting_module
def execute()
  reload(supporting_module)
  ...

This way every time you run the tool you'll be sure to have the updated module. Once development is done, this can be taken out.

4
  • 1
    Extremely helpful and this does solve the simple example I posted. However, if supporting_module also imports other helper modules, then they would need to be called with reload as well. I'm thinking this is the best we'll get as it makes sense with the way Python loads modules - I'll mark yours as the answer if something more robust doesn't come along in the next day or so.
    – Josh Werts
    Commented Mar 27, 2014 at 19:12
  • 1
    Very helpful. I have found that it is sufficient to call reload() right after importing the supporting module, at the top of the toolbox's PYT file. Thus, if the toolbox contains several tools, I do not have to duplicate the reload in each tool's execute() function.
    – Mike Finch
    Commented Aug 31, 2017 at 15:19
  • I don't really understand how this anwser solves the issue. I have my Tools stored in .py files and I import them using from pyfile import Tool, adding reload(tool) returns typeError: reload() argument must be module. Any ideas?
    – Ratnanil
    Commented Feb 6, 2018 at 22:29
  • 3
    I found the solution! import the py-File as a Module first, reload and then import the class within the module. See: stackoverflow.com/a/6946467/4139249
    – Ratnanil
    Commented Feb 14, 2018 at 22:42
5

Here is a different and more robust way than I suggested before.

I haven't used this module myself, but I think it would solve your problem:

Python Module Reloader

This library implements a dependency-based module reloader for Python. Unlike the builtin reload() function, this reloader will reload the requested module and all other modules that are dependent on that module.

Given the previous example, this should load all the dependancies with one call:

import reloader
reloader.enable()

import supporting_module

def execute()
  reloader.reload(supporting_module)
  ...

This is the first time I've noticed this module, so if you implement it in your tools, comment back on how well it works for you.

3
  • Good find! This worked if added exactly as you show. I only have to right-click -> refresh on the toolbox if the .pyt file itself changes. Supporting modules and their sub modules are being reloaded successfully. One caveat though - I did get a couple unexplained exceptions on occasion and an ArcMap crash (though ArcMap crashes really aren't that abnormal, unfortunately). Note - I've only tested this with the supporting module and one dependent sub-module. May report back on stability in the future.
    – Josh Werts
    Commented Mar 28, 2014 at 12:55
  • 1
    Super, thanks for reporting back. Yeah, when driving ArcMap, I sometimes feel like a Crash Test Dummy.
    – cndnflyr
    Commented Mar 28, 2014 at 16:51
  • Unfortunately, I've run into way too many stability issues with this reloader, so I've changed the selected answer to the original reload() built-in function. Sometimes simpler is better!
    – Josh Werts
    Commented Mar 31, 2014 at 20:35
0

Here's an example of refreshing a tool imported from a different folder in ArcGIS Pro using Python 3.7

# Import the module to support refreshing
import tools.MyNewTool as MyNewToolModule
# Import the tool
from tools.MyNewTool import MyNewTool

class Toolbox(object):
    def __init__(self):
        """
        Define the toolbox (the name of the toolbox is the name of the .pyt file).
        """
        self.label = "Toolbox"
        self.alias = "toolbox"

        # Reload the MyNewToolModule to ensure all properties are updated
        importlib.reload(MyNewToolModule)

        # List of tool classes associated with this toolbox
        self.tools = [MyNewTool]
        
    # Rest of the code ...
0

Here's how I accomplished it, but it should be noted that while this may make development easy, it creates a slew of complications after development:

  1. You'll need to remove importlib.reload() in order to successfully stage the tool when publishing to ArcGIS Server
  2. Even after successfully publishing to ArcGIS Server, the tool will fail with ERROR 000816: The tool is not valid because the imported module is unrecognized by the server: https://support.esri.com/en-us/knowledge-base/error-error-000816-the-tool-is-not-valid-000023081

Frankly I'm not sure the best way to build a large toolbox and isolate functionality into other modules if the goal is to publish to ArcGIS Server, so I've now resorted to leaving all functionality within the .pyt (welcome any solutions). I'm using ArcGIS Pro 3.1.3 and ArcGIS Server 11.0

import importlib
import SupportingModule
importlib.reload(SupportingModule)

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = "Toolbox"

        # List of tool classes associated with this toolbox
        self.tools = [Tool]

class Tool(object):
    def __init__(self):
        self.label = "Tool"
        self.description = "Description"
        self.canRunInBackground = False

    def getParameterInfo(self):
        return

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        SupportingModule.some_function(parameters)
        return

    def postExecute(self, parameters):
        return

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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