<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools in Python Questions</title>
    <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277598#M67399</link>
    <description>&lt;P&gt;Fundamentally confused.&amp;nbsp; I made a Python Script Toolbox with multiple tools (hopefully proper terminology):&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="script_tool.jpg" style="width: 200px;"&gt;&lt;img src="https://community.esri.com/t5/image/serverpage/image-id/67710iF522B99AC11C8BFD/image-size/small?v=v2&amp;amp;px=200" role="button" title="script_tool.jpg" alt="script_tool.jpg" /&gt;&lt;/span&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Each tool works fine, and the basic Python code/structure is as shown below.&amp;nbsp; NOTE! I am only showing one of the Tools (&lt;EM&gt;&lt;STRONG&gt;metadata_mcm)&lt;/STRONG&gt;&lt;/EM&gt;&lt;EM&gt; and not&amp;nbsp;&lt;STRONG&gt;update_attr &lt;/STRONG&gt;&lt;/EM&gt;or&lt;EM&gt;&lt;STRONG&gt; xml_element_csv_creator&amp;nbsp;&lt;/STRONG&gt;&lt;/EM&gt;- but they are the same in basic structure.&amp;nbsp; Skip to my questions below before diving into code.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;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 = [metadata_mcm, create_xml_element_csv, update_attr]

class ToolValidator(object):
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  def __init__(self):
    """Setup arcpy and the list of tool parameters."""
    self.params = arcpy.GetParameterInfo()

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""
    return

  def updateParameters(self):
    """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):
    """Modify the messages created by internal validation for each tool
    parameter.  This method is called after internal validation."""
    self.params[1].clearMessage()
    if self.params[1].value is None:
        self.params[1].clearMessage()
    else:
        if os.path.exists(self.params[1]):
            self.params[1].setErrorMessage('output path already exists')
        else:
            self.params[1].clearMessage()

    return
class create_xml_element_csv(object):
    def __init__(self):
        self.label = "xml element csv creator"
        self.desciption = "create inventory of xml elements relevant to fields"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        param0 = arcpy.Parameter(
            displayName="Input Feature",
            name="fc_in",
            datatype="Layer",
            parameterType="Required",
            direction="Input")
        param1 = arcpy.Parameter(
            displayName="Path/to/file.csv",
            name="fp_csv",
            datatype="DEFile",
            parameterType="Required",
            direction="Input")
        params = []
        params.append(param0)
        params.append(param1)
        return params

    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."""

    def execute(self, parameters, messages):
        """The source code of the tool."""
        fc = parameters[0]
        desc = arcpy.Describe(fc)
        fp_fc = desc.featureClass.catalogPath
        fp_csv = parameters[1].valueAsText
        st0, st1 = os.path.splitext(fp_csv)
        # csv
        if st1 == '.csv':
            pass
        else:
            fp_csv = '{}.csv'.format(fp_csv)

        flds = [f.name for f in arcpy.ListFields(fp_fc)]
        vals = np.column_stack([flds, [None] * len(flds), [None] * len(flds), [None] * len(flds)])
        # attrdef = definition
        # attrdefs = definition source
        cn = ['attrlabl', 'attralias', 'attrdef', 'attrdefs']
        df_fields = pd.DataFrame(vals, columns=cn)
        df_fields.to_csv(fp_csv)
        return&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I don't understand a couple things:&lt;/P&gt;&lt;P&gt;1) Can the &lt;STRONG&gt;&lt;EM&gt;ToolValidator Class&lt;/EM&gt;&lt;/STRONG&gt; work with multiple tools?&amp;nbsp; It's odd that I have essentially hard-coded the paramaters positionally in &lt;STRONG&gt;&lt;EM&gt;updateMessages&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt;&amp;nbsp;&lt;/EM&gt;for ALL subsequent tools.&amp;nbsp; I want to ensure that the output file does not exist prior to running tool.&amp;nbsp; This is argument 1 for&amp;nbsp;&lt;EM&gt;&lt;STRONG&gt;create_xml_element_csv&lt;/STRONG&gt;&lt;/EM&gt; but doesn't exist for the two other tools.&amp;nbsp; So can I create a &lt;STRONG&gt;&lt;EM&gt;ToolValidator Class&lt;/EM&gt; &lt;/STRONG&gt;and only apply the validation to one tool in the toolbox?&lt;/P&gt;&lt;P&gt;2) Is my syntax within&amp;nbsp;&lt;EM&gt;&lt;STRONG&gt;ToolValidator/updateMessages&lt;/STRONG&gt;&lt;/EM&gt;&lt;EM&gt; method correct?&amp;nbsp; Found this syntax from a StackExchange response.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;The tools run BUT the validation of&lt;/P&gt;&lt;PRE&gt;&lt;SPAN&gt;if &lt;/SPAN&gt;os.path.exists(&lt;SPAN&gt;self&lt;/SPAN&gt;.params[&lt;SPAN&gt;1&lt;/SPAN&gt;]):&lt;BR /&gt;    &lt;SPAN&gt;self&lt;/SPAN&gt;.params[&lt;SPAN&gt;1&lt;/SPAN&gt;].setErrorMessage(&lt;SPAN&gt;'output path already exists'&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;P&gt;is not being performed.&amp;nbsp; I can pass an argument of a path that exists, and no warming is issued, tool overwrites the existing file.&amp;nbsp; No bueno.&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Thanks,&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Zach&lt;/EM&gt;&lt;/P&gt;</description>
    <pubDate>Tue, 11 Apr 2023 23:13:03 GMT</pubDate>
    <dc:creator>ZacharyUhlmann1</dc:creator>
    <dc:date>2023-04-11T23:13:03Z</dc:date>
    <item>
      <title>Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277598#M67399</link>
      <description>&lt;P&gt;Fundamentally confused.&amp;nbsp; I made a Python Script Toolbox with multiple tools (hopefully proper terminology):&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="script_tool.jpg" style="width: 200px;"&gt;&lt;img src="https://community.esri.com/t5/image/serverpage/image-id/67710iF522B99AC11C8BFD/image-size/small?v=v2&amp;amp;px=200" role="button" title="script_tool.jpg" alt="script_tool.jpg" /&gt;&lt;/span&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Each tool works fine, and the basic Python code/structure is as shown below.&amp;nbsp; NOTE! I am only showing one of the Tools (&lt;EM&gt;&lt;STRONG&gt;metadata_mcm)&lt;/STRONG&gt;&lt;/EM&gt;&lt;EM&gt; and not&amp;nbsp;&lt;STRONG&gt;update_attr &lt;/STRONG&gt;&lt;/EM&gt;or&lt;EM&gt;&lt;STRONG&gt; xml_element_csv_creator&amp;nbsp;&lt;/STRONG&gt;&lt;/EM&gt;- but they are the same in basic structure.&amp;nbsp; Skip to my questions below before diving into code.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;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 = [metadata_mcm, create_xml_element_csv, update_attr]

class ToolValidator(object):
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  def __init__(self):
    """Setup arcpy and the list of tool parameters."""
    self.params = arcpy.GetParameterInfo()

  def initializeParameters(self):
    """Refine the properties of a tool's parameters.  This method is
    called when the tool is opened."""
    return

  def updateParameters(self):
    """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):
    """Modify the messages created by internal validation for each tool
    parameter.  This method is called after internal validation."""
    self.params[1].clearMessage()
    if self.params[1].value is None:
        self.params[1].clearMessage()
    else:
        if os.path.exists(self.params[1]):
            self.params[1].setErrorMessage('output path already exists')
        else:
            self.params[1].clearMessage()

    return
class create_xml_element_csv(object):
    def __init__(self):
        self.label = "xml element csv creator"
        self.desciption = "create inventory of xml elements relevant to fields"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        param0 = arcpy.Parameter(
            displayName="Input Feature",
            name="fc_in",
            datatype="Layer",
            parameterType="Required",
            direction="Input")
        param1 = arcpy.Parameter(
            displayName="Path/to/file.csv",
            name="fp_csv",
            datatype="DEFile",
            parameterType="Required",
            direction="Input")
        params = []
        params.append(param0)
        params.append(param1)
        return params

    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."""

    def execute(self, parameters, messages):
        """The source code of the tool."""
        fc = parameters[0]
        desc = arcpy.Describe(fc)
        fp_fc = desc.featureClass.catalogPath
        fp_csv = parameters[1].valueAsText
        st0, st1 = os.path.splitext(fp_csv)
        # csv
        if st1 == '.csv':
            pass
        else:
            fp_csv = '{}.csv'.format(fp_csv)

        flds = [f.name for f in arcpy.ListFields(fp_fc)]
        vals = np.column_stack([flds, [None] * len(flds), [None] * len(flds), [None] * len(flds)])
        # attrdef = definition
        # attrdefs = definition source
        cn = ['attrlabl', 'attralias', 'attrdef', 'attrdefs']
        df_fields = pd.DataFrame(vals, columns=cn)
        df_fields.to_csv(fp_csv)
        return&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I don't understand a couple things:&lt;/P&gt;&lt;P&gt;1) Can the &lt;STRONG&gt;&lt;EM&gt;ToolValidator Class&lt;/EM&gt;&lt;/STRONG&gt; work with multiple tools?&amp;nbsp; It's odd that I have essentially hard-coded the paramaters positionally in &lt;STRONG&gt;&lt;EM&gt;updateMessages&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt;&amp;nbsp;&lt;/EM&gt;for ALL subsequent tools.&amp;nbsp; I want to ensure that the output file does not exist prior to running tool.&amp;nbsp; This is argument 1 for&amp;nbsp;&lt;EM&gt;&lt;STRONG&gt;create_xml_element_csv&lt;/STRONG&gt;&lt;/EM&gt; but doesn't exist for the two other tools.&amp;nbsp; So can I create a &lt;STRONG&gt;&lt;EM&gt;ToolValidator Class&lt;/EM&gt; &lt;/STRONG&gt;and only apply the validation to one tool in the toolbox?&lt;/P&gt;&lt;P&gt;2) Is my syntax within&amp;nbsp;&lt;EM&gt;&lt;STRONG&gt;ToolValidator/updateMessages&lt;/STRONG&gt;&lt;/EM&gt;&lt;EM&gt; method correct?&amp;nbsp; Found this syntax from a StackExchange response.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;The tools run BUT the validation of&lt;/P&gt;&lt;PRE&gt;&lt;SPAN&gt;if &lt;/SPAN&gt;os.path.exists(&lt;SPAN&gt;self&lt;/SPAN&gt;.params[&lt;SPAN&gt;1&lt;/SPAN&gt;]):&lt;BR /&gt;    &lt;SPAN&gt;self&lt;/SPAN&gt;.params[&lt;SPAN&gt;1&lt;/SPAN&gt;].setErrorMessage(&lt;SPAN&gt;'output path already exists'&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;P&gt;is not being performed.&amp;nbsp; I can pass an argument of a path that exists, and no warming is issued, tool overwrites the existing file.&amp;nbsp; No bueno.&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Thanks,&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Zach&lt;/EM&gt;&lt;/P&gt;</description>
      <pubDate>Tue, 11 Apr 2023 23:13:03 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277598#M67399</guid>
      <dc:creator>ZacharyUhlmann1</dc:creator>
      <dc:date>2023-04-11T23:13:03Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277621#M67400</link>
      <description>&lt;P&gt;The&amp;nbsp;ToolValidator class is used by script tools in custom toolboxes (legacy .tbx or new .atbx), not by Python Toolboxes (.pyt).&lt;/P&gt;&lt;P&gt;In python toolboxes (.pyt) all the validation is done is the &lt;SPAN class=""&gt;updateMessages&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;and&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN class=""&gt;updateParameters&lt;/SPAN&gt;&amp;nbsp;methods of the tool class itself&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;A href="https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/customizing-tool-behavior-in-a-python-toolbox.htm" target="_self"&gt;Customize tool behavior in a Python toolbox&lt;/A&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 12 Apr 2023 01:13:32 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277621#M67400</guid>
      <dc:creator>Luke_Pinner</dc:creator>
      <dc:date>2023-04-12T01:13:32Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277759#M67401</link>
      <description>&lt;P&gt;Another thing is that each tool has an updateMessages function. Normally validation is done on a per-tool basis using that tool's updateParameters and updateMessages.&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 12 Apr 2023 12:52:01 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277759#M67401</guid>
      <dc:creator>AlfredBaldenweck</dc:creator>
      <dc:date>2023-04-12T12:52:01Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277854#M67403</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.esri.com/t5/user/viewprofilepage/user-id/10780"&gt;@Luke_Pinner&lt;/a&gt;&amp;nbsp;- I appreciate your response.&amp;nbsp; I suspected that may be the case.&amp;nbsp; ESRI's documentation can be really frustrating.&amp;nbsp; I scoured that link you included and all links therein prior to posting.&amp;nbsp; Reread them just now and it's still really confusing.&amp;nbsp; FYI - I am experienced with Object Oriented Programming and Classes.&amp;nbsp; None of the links on that page show me a fleshed out implementation of parameter validation:&lt;/P&gt;&lt;P&gt;1) &lt;A href="https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/understanding-validation-in-script-tools.htm" target="_self"&gt;understanding-validation-in-script-tools&lt;/A&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;2)&amp;nbsp;&lt;A href="https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/programming-a-toolvalidator-class.htm" target="_self"&gt;programming-a-toolvalidator-class&lt;/A&gt;&amp;nbsp;which has instructions on constructing the ToolValidator Class, which I thought were not applicable with the Python Toolbox&lt;/P&gt;&lt;P&gt;So! In the link you sent, they provide this example (below).&amp;nbsp; But I'm confused.&amp;nbsp; What is the&amp;nbsp;&lt;STRONG&gt;parameters&lt;/STRONG&gt; argument?&amp;nbsp; I imagine that is&amp;nbsp;&lt;STRONG&gt;params&lt;/STRONG&gt;?&amp;nbsp; And more confusingly, what is parameters[&lt;STRONG&gt;6&lt;/STRONG&gt;]?&amp;nbsp;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;if not parameters[6].altered&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;The example preceding this has only 3 parameters, so 0,1,2 indices.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;def updateParameters(self, parameters):
    # Set the default distance threshold to 1/100 of the larger of the width
    #  or height of the extent of the input features.  Do not set if there is no 
    #  input dataset yet, or the user has set a specific distance (Altered is true).
    #
    if parameters[0].valueAsText:
        if not parameters[6].altered:
            extent = arcpy.Describe(parameters[0]).extent
        if extent.width &amp;gt; extent.height:
            parameters[6].value = extent.width / 100
        else:
            parameters[6].value = extent.height / 100

    return&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I'm REALLY appreciative of your help!&amp;nbsp; Just confused and critical of the ESRI documentation that always seems to weave in and out of Pro vs Desktop vs whatever.&amp;nbsp; Check reply below - I did solve this.&amp;nbsp; It could be ugly.&amp;nbsp; Suggestions welcome...&lt;/P&gt;</description>
      <pubDate>Wed, 12 Apr 2023 16:18:17 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277854#M67403</guid>
      <dc:creator>ZacharyUhlmann1</dc:creator>
      <dc:date>2023-04-12T16:18:17Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277864#M67404</link>
      <description>&lt;UL&gt;&lt;LI&gt;"parameters" is what params from&amp;nbsp;getParameterInfo() is called in as. Yeah, it'd be better if they were the same word, but whatever. You can probably change one to the other for consistency; I haven't tried it, but I bet it'd be fine.&lt;BR /&gt;&lt;BR /&gt;&lt;/LI&gt;&lt;LI&gt;In the case you're referencing, there are at least 7 parameters in their list of parameters. It's checking to see if there's a value in the first parameter (parameters[0]) before validating the seventh parameter (parameters[6]).&lt;UL&gt;&lt;LI&gt;Generally, each example on a documentation page lives in a vaccuum and is not related to any other example unless they're in the same section. So any example in the &lt;STRONG&gt;values&lt;/STRONG&gt; section is probably related to others in that section, but not to examples in the&amp;nbsp;&lt;STRONG&gt;depedencies&lt;/STRONG&gt; section.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;&lt;SPAN&gt;You don't need a separate Tool Validator class because its stuff is already inside the tool already. Its&amp;nbsp;functionality is used on a per-tool basis in their&amp;nbsp;updateParameters() and updateMessages().&amp;nbsp; (See lines 47 and 64 below)&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;Use the Validator reference page as you were already but populate inside the tool instead and you should be golden.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;I find it helpful to assign each parameter in the list as a variable during validation so I can keep track of what I'm doing to each one, as well as making it easier to change their numbering later if I need.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;Below is a pared down example of one of the tools in one of my PYTs.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;Each tool is listed in self.tools of the Toolbox class, and under each tool is the validation.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;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 = ""

        # List of tool classes associated with this toolbox
        self.tools = [ExportPhotos]

class ExportPhotos(object): # The tool
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Export Photos"
        self.description = ""
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        param0 = arcpy.Parameter(
                    displayName="GDBs or Feature Classes?",
                    name="GDBs_or_Feature_Classes?",
                    datatype="GPString",
                    parameterType="Required",
                    direction="Input")
        param0.filter.type = "ValueList"
        param0.filter.list = ["GDBs", "Feature Classes"]
        param0.value = "GDBs"
        
        param1 = arcpy.Parameter(
                    displayName="Input GDBs",
                    name="Input_GDBs",
                    datatype="DEWorkspace",
                    parameterType="Optional",
                    direction="Input",
                    multiValue=True)
                    
        param2 = arcpy.Parameter(
                    displayName="Input Feature Classes",
                    name="Input_FCs",
                    datatype="GPFeatureLayer",
                    parameterType="Optional",
                    direction="Input",
                    multiValue=True)
        params = [param0, param1, param2) 
        return params

    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."""
        gdbOrFC = parameters[0] #param0
        inGDBS = parameters[1] #param1
        inFCs = parameters[2] #param2
        
        #Determines if inFCs or inGDBs is visible
        if gdbOrFC.value== "GDBs":
            inGDBS.enabled = True #inGDBS
            inFCs.enabled = False #inFCs        
        else:
            inGDBS.enabled = False
            inFCs.enabled = True 
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        gdbOrFC = parameters[0] #param0
        inGDBS = parameters[1] #param1
        inFCs = parameters[2] #param2
        
        #Set inGDBs to pretend it's required
        if (gdbOrFC.value == "GDBs") and (inGDBS.value is None):
            inGDBS.setIDMessage('ERROR', 735)
        return&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 12 Apr 2023 16:14:10 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277864#M67404</guid>
      <dc:creator>AlfredBaldenweck</dc:creator>
      <dc:date>2023-04-12T16:14:10Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277873#M67405</link>
      <description>&lt;P&gt;Tentatively found a solution.&amp;nbsp; Any alternatives to the conditional logic:&amp;nbsp;&lt;EM&gt;if parameters[1].value&amp;nbsp;&lt;/EM&gt;would be greatly appreciated!&amp;nbsp; But it works.&amp;nbsp; Code is commented to explain why I did this.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        # This if statement required or else error message will for parameter[0]
        # "TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType".
        # Apparently this method will run through ALL parameters right off the bat.  So add
        # conditional statements to ensure validation only performed on target parameters i.e. parameter[1] in this case
        if parameters[1].value:
            fp_csv = parameters[1].valueAsText
            if os.path.exists(fp_csv):
                parameters[1].setErrorMessage('output path already exists')
            else:
                parameters[1].clearMessage()
        return&lt;/LI-CODE&gt;&lt;P&gt;Note that when an existing path/to/file.csv is passed for argument 2 (parameters[1]), the message will display:&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="script_tool_2.jpg" style="width: 999px;"&gt;&lt;img src="https://community.esri.com/t5/image/serverpage/image-id/67778i295D44B68E79B6D4/image-size/large?v=v2&amp;amp;px=999" role="button" title="script_tool_2.jpg" alt="script_tool_2.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;</description>
      <pubDate>Wed, 12 Apr 2023 16:34:31 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1277873#M67405</guid>
      <dc:creator>ZacharyUhlmann1</dc:creator>
      <dc:date>2023-04-12T16:34:31Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1278248#M67409</link>
      <description>&lt;P&gt;I don't like hard coding the array indexes since they are a pain to adjust if you move around the order of the parameters. So I wrote a function to get them by name and call that at the start of the update and execute functions.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;# Find the parameter by name (which is set in getParametmerInfo)
def get_parm (parms, name, enforce_not_null = False):
    for parm in parms:
        if parm.name == name:
            if enforce_not_null and (parm.value is None or parm.valueAsText == ''):
                raise Exception ("'%s' has an invalid value" % (parm.displayName))
            return parm 

# At the start of update and execute functions, retrieve the relevant parameters by name instead of index
def execute (self, parameters):
        gdbOrFC = get_parm (parameters, "GDBs_or_Feature_Classes?")
        inGDBS = get_parm (parameters, "Input_GDBs")
        inFCs = get_parm (parameters, "Input_FCs")&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 13 Apr 2023 13:52:12 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1278248#M67409</guid>
      <dc:creator>DonMorrison1</dc:creator>
      <dc:date>2023-04-13T13:52:12Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1282162#M67508</link>
      <description>&lt;P&gt;Perhaps a new post is in order.&amp;nbsp; Do either of you have experience defining an argument as a&amp;nbsp;&lt;STRONG&gt;DETextFile&amp;nbsp;&lt;/STRONG&gt;or&amp;nbsp;&lt;STRONG&gt;DEFile&lt;/STRONG&gt;?&amp;nbsp; For another Python Toolbox built on the same template you all helped me with (thanks!), I want to pass a path/to/csv as an argument.&amp;nbsp; Spent the hour fleshing out, copying and pasting the .pyt.&amp;nbsp; Looks good.&amp;nbsp; Tool opens, but when I select (or try to select) the input csv in both the third and fourth arguments, it doesn't load - argument input box(es) stay blank.&amp;nbsp; I've tried manually copying the path/to/file.csv into the argument,&amp;nbsp;&lt;STRONG&gt;csv GIS attribute table&lt;/STRONG&gt; in this case.&amp;nbsp; Same thing.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="DEfolder_pytoolbox.jpg" style="width: 640px;"&gt;&lt;img src="https://community.esri.com/t5/image/serverpage/image-id/68989iF0F776035D5DF0CA/image-size/large?v=v2&amp;amp;px=999" role="button" title="DEfolder_pytoolbox.jpg" alt="DEfolder_pytoolbox.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;When I try running the tool I, an error is thrown about&amp;nbsp;&lt;STRONG&gt;NoneType&lt;/STRONG&gt; as would be expected.&amp;nbsp; My first instinct is to think an ESRI bug, but I'll withhold judgement until digging.&amp;nbsp; Just trying to pass a file as an argument.&lt;/P&gt;</description>
      <pubDate>Mon, 24 Apr 2023 23:28:42 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1282162#M67508</guid>
      <dc:creator>ZacharyUhlmann1</dc:creator>
      <dc:date>2023-04-24T23:28:42Z</dc:date>
    </item>
    <item>
      <title>Re: Constructing the ToolValidator Class to Validate Inputs in a Python Script Tool containing Multiple Tools</title>
      <link>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1314684#M68322</link>
      <description>&lt;P&gt;Super late response, but I had the same problem yesterday and it was because my two parameters had the same name.&lt;/P&gt;</description>
      <pubDate>Wed, 02 Aug 2023 17:15:52 GMT</pubDate>
      <guid>https://community.esri.com/t5/python-questions/constructing-the-toolvalidator-class-to-validate/m-p/1314684#M68322</guid>
      <dc:creator>AlfredBaldenweck</dc:creator>
      <dc:date>2023-08-02T17:15:52Z</dc:date>
    </item>
  </channel>
</rss>

