Select to view content in your preferred language

Python Toolbox's Tool methods "self" object?

943
5
Jump to solution
02-17-2023 10:23 AM
JoanneMcGraw
Occasional Contributor III

Hello all,

I'm hoping someone can tell me what the "self" is referring to in a Toolbox's Tool's method definitions. It's not what I expect it to be.

Below is an example with regards to a property set on the Tool object in its getParameterInfo method using "self". The following is the smallest example I could come up with to illustrate the behaviour which is unexpected to me. I can perform the following steps in both ArcPro 3.0.2 and ArcCatalog 10.4.1 with the same results, so this is not new. The code I provide at the end of this post will run in either product as is.

1. In ArcPro or ArcCatalog (or ArcMap, I assume), create a new Python Toolbox to generate a pyt file that includes default class definitions for Toolbox and Tool objects. The following code changes are all within the Tool class' definition.
2. Add a version property to Tool and set its value to 1;
3. In Tool's getParameterInfo(self) method, add an arcpy.Parameter to ensure its code is running and before returning the parameter, set self.version to 100;
4. In Tool's execute method, add an arcpy message to print out the value in self.version.
5. Run the tool. No need to make any changes in its GUI, just load it, run it and look at its output messages.

When I run this tool, the value that is output by the "print" message is 1. I expect it to be 100.

As a further test, I put "self.version = " different values in updateParameters and updateMessages and tried changing the default value in the GUI's Version input box to get them to run. Still, when I run the task, the output tells me the value in self.version is 1.

Finally, in Tool's __init__ method, I set self.version to 2. That line is in the code below also, but is currently commented out. Uncomment it to try it yourself. This time, the "print" output 2, so updating the self.version property in the __init__ method works as I expect but not when done in any other Tool method.

Can anyone explain why it is not outputing 100 in my original test above? What is "self" referring to in those the Tool class methods' parameter lists? Or this there something wrong with how I am tryign to use it?

Regards,

# -*- coding: utf-8 -*-

import arcpy

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):

    # ------------------------------------------
    # Added and initialized this property
    # ------------------------------------------
    version = 1

    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Tool"
        self.description = ""
        self.canRunInBackground = False

        # -----------------------------------------
        # Added this line
        # -----------------------------------------
        # self.version = 2

    def getParameterInfo(self):
        """Define parameter definitions"""

        # ------------------------------------------
        # This whole method is new
        # ------------------------------------------
        # params = None
        # return params
        version_param = arcpy.Parameter(
            displayName="Version",
            name="version",
            datatype="GPString",
            parameterType="Optional",
            direction="Input")
        version_param.value = "testing, testing, 123 ..."
        self.version = 100
        return [version_param]

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""

        # ------------------------------------------
        # Added this line
        # ------------------------------------------
        arcpy.AddMessage("execute: " + str(self.version))

        return

    def postExecute(self, parameters):
        """This method takes place after outputs are processed and
        added to the display."""
        return

 

1 Solution

Accepted Solutions
JohannesLindner
MVP Frequent Contributor

I have run into the same problem in the past.

The way I dealt with this is defining an empty class in the toolbox. You can set and get class attributes for this class in the tool methods.

class Toolbox:
    def __init__(self):
        self.label = "Toolbox"
        self.alias = "toolbox"
        self.tools = [Tool]


class ToolboxData:
    pass
    

class Tool:
    def __init__(self):
        self.label = "Tool"
        self.canRunInBackground = False
        
    def getParameterInfo(self):
        version_param = arcpy.Parameter(
            displayName="Version",
            name="version",
            datatype="GPString",
            parameterType="Optional",
            direction="Input")
        version_param.value = "testing, testing, 123 ..."
        ToolboxData.version = 100
        return [version_param]
        
        
    def execute(self, parameters, messages):
        arcpy.AddMessage("execute: " + str(ToolboxData.version))

 


Have a great day!
Johannes

View solution in original post

0 Kudos
5 Replies
by Anonymous User
Not applicable

You are setting version as a Class Attribute, which acts a little differently than a property and would need to referred to with the Class Name.version, so Tool.version = 100. Think it has something to do with how the tools are ingested into the apps pipeline.

 

by Anonymous User
Not applicable

After thinking about this some more, you are also trying to set an attribute in a getter method that is designed to just return a list of parameters for the UI or in code.  If you want to change the version (either class or instance), I think you need to do this in the execute method:

 

class Tool(object):

    # ------------------------------------------
    # Added and initialized this property
    # ------------------------------------------
    ClassVersion = 1

    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Tool"
        self.description = ""
        self.canRunInBackground = False
        # -----------------------------------------
        # Added this line
        # -----------------------------------------
        self.version = 1

        
    def getParameterInfo(self):
        """Define parameter definitions"""

        # ------------------------------------------
        # This whole method is new
        # ------------------------------------------
        # params = None
        # return params
        version_param = arcpy.Parameter(
            displayName="Version",
            name="version",
            datatype="GPString",
            parameterType="Optional",
            direction="Input")
        version_param.value = "testing, testing, 123 ..."

        return [version_param]
    ...

    def execute(self, parameters, messages):
        """The source code of the tool."""

        # ------------------------------------------
        # Added this line
        # ------------------------------------------
        arcpy.AddMessage("pre execute Tool.version: " + str(self.ClassVersion))
        arcpy.AddMessage("pre execute self.version1: " + str(self.version))
        self.version = 100
        Tool.ClassVersion = 300
        arcpy.AddMessage("execute Tool.version: " + str(self.ClassVersion))
        arcpy.AddMessage("execute self.version1: " + str(self.version))
        return

    ...

 

 

Results:

Start Time: Sunday, February 19, 2023 2:29:28 PM
pre execute Tool.version: 1
pre execute self.version1: 1
execute Tool.version: 300
execute self.version1: 100
Succeeded at Sunday, February 19, 2023 2:29:28 PM (Elapsed Time: 0.03 seconds)

 

 

0 Kudos
JoanneMcGraw
Occasional Contributor III

Thank you for your responses, Jeff!

I recognize that what I am doing here is odd but it was only meant to illustrate what I was running into in the simplest example possible. In reality, I'm using information from a couple of places to determine which parameters are actually being added to the tool at runtime and don't want to have to duplicate the exact same code in the execute to figure out which parameters to check for input values and how to interpret them. What's in parameter 2 during one execution is not necessarily in parameter 2 the next time...it might be in parameter 3.

The information in your first post, that I was employing a Class Attribute, rather than a property was useful to know. I think I might be getting languages mixed up because I thought I remembered defining classes that way in Python before but I'm sure not now.

In the end, I went with Johannes suggestion. I have a number of these values that I want to store while building the parameter list and that seemed the least complicated and most easily understood by anyone who has to update this code later.

Cheers

jtm

0 Kudos
JohannesLindner
MVP Frequent Contributor

I have run into the same problem in the past.

The way I dealt with this is defining an empty class in the toolbox. You can set and get class attributes for this class in the tool methods.

class Toolbox:
    def __init__(self):
        self.label = "Toolbox"
        self.alias = "toolbox"
        self.tools = [Tool]


class ToolboxData:
    pass
    

class Tool:
    def __init__(self):
        self.label = "Tool"
        self.canRunInBackground = False
        
    def getParameterInfo(self):
        version_param = arcpy.Parameter(
            displayName="Version",
            name="version",
            datatype="GPString",
            parameterType="Optional",
            direction="Input")
        version_param.value = "testing, testing, 123 ..."
        ToolboxData.version = 100
        return [version_param]
        
        
    def execute(self, parameters, messages):
        arcpy.AddMessage("execute: " + str(ToolboxData.version))

 


Have a great day!
Johannes
0 Kudos
JoanneMcGraw
Occasional Contributor III

Thanks to both Jeff and Johannes for responding to my questions. All their responses to my post were informative and useful. 

In the end, I went with Johannes suggestion because it seemed the least complicated and most easily understood by anyone who has to maintain the tool I am creating later.

Thank you!

jtm

0 Kudos