Geoprocessing tool UpdateParameters when running from command line

1320
4
02-25-2020 09:27 AM
PatrickHill
New Contributor II

I have a gp tool that updates the output feature class names in the updateParameters() method. The tool runs fine from Pro. However, I am setting up functional tests for the tool that must run from the command line. When running the test I get an error that the output cannot be created. It references the name of the output set by updateParameters but does not have the full output path (which is implied when setting in the tool)

def updateParameters(self, parameters):

   if parameters[0].value is not None and parameters[1].value is not None:
   name = self.get_name(parameters[0].valueAsText, parameters[1].valueAsText)
   parameters[4].value = name
   corridor_name = self.get_corridor_name(parameters[0].valueAsText, parameters[1].valueAsText)
   parameters[3].value = corridor_name

def get_name(self, planting_path, as_applied_path):
   planting_base = os.path.basename(planting_path)
   as_applied_base = os.path.basename(as_applied_path)
   split = planting_base.split('_')
   prefix = '{}_{}'.format(split[0], split[1])
   operation = '{}_{}'.format(split[2], split[3])
   po = '{}_{}'.format(prefix, operation)
   as_applied = as_applied_base.replace(po, "")
   name = '{}{}{}'.format(po, '_planting_modified', as_applied)
   return name

def get_corridor_name(self, planting_path, as_applied_path):
   planting_base = os.path.basename(planting_path)
   as_applied_base = os.path.basename(as_applied_path)
   split = planting_base.split('_')
   prefix = '{}_{}'.format(split[0], split[1])
   operation = '{}_{}'.format(split[2], split[3])
   po = '{}_{}'.format(prefix, operation)
   as_applied = as_applied_base.replace(po, "")
   name = '{}{}{}'.format(po, '_corridors', as_applied)
   return name

When running from my test, I set output feature names explicitly and execute the tool from arcpy. The test fails because it cannot resolve the output names which it overwrites when executing updateParameters() because it does not have full paths to the output feature class location. If I comment out where I set the value in updateParameters, the test runs fine.  Here is my test:

class TestFindPointOverlapTestCase(unittest.TestCase, arcpyAssert.FeatureClassAssertMixin):

   @classmethod
   def setUpClass(self):
      try:
         toolboxdir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'Tools')
         self.toolbox_under_test = os.path.join(toolboxdir, 'toolboxes', 'TrialDataPrep.pyt')
         self.extract_to = testutils.get_temp_folder()
         zip_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'testdata.zip')
         input_gdb = testutils.extract_zipped_gdb(zip_path, self.extract_to, 'FindPointOverlapData.gdb')
         self.input_target = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_Planting')
         self.input_compare = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_asapplied_flow1')
         self.target_id_field = 'Trial_0_1'
         output_gdb = testutils.create_output_gdb(self.extract_to)
   

         self.output_features = os.path.join(output_gdb, 'test_points') #this path overwritten by updateParameters()

         self.output_areas = os.path.join(output_gdb, 'test_areas') #this path overwritten by updateParameters()


         self.compare_pts = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_planting_modified_asapplied_flow1')
         self.compare_areas = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_corridors_asapplied_flow1')
      except Exception:
         super(TestFindPointOverlapTestCase, self).tearDownClass()

  

   @classmethod
   def tearDownClass(self):
      testutils.delete_output_gdb(self.extract_to)

   def testFindPointOverlap(self):
      arcpy.ImportToolbox(self.toolbox_under_test)
      print('output_areas = ' + self.output_areas)
      arcpy.PointOverlap_trials(self.input_target, self.input_compare, self.target_id_field, self.output_areas,                                              self.output_features)

I'm a little confused because I was under the impression that updateParameters is only run when a toolbox parameter is changed in the dialog and should not be an issue when running from command line or script. Below is the error I am getting:

======================================================================
ERROR: testFindPointOverlap (tests.testFindPointOverlapTestCase.TestFindPointOverlapTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\patri\bitbucket\in10t-gp-tools\test\tests\testFindPointOverlapTestCase.py", line 43, in testFindPointOverlap
arcpy.PointOverlap_trials(self.input_target, self.input_compare, self.target_id_field, self.output_areas, self.output_features)
File "C:\Users\patri\bitbucket\in10t-gp-tools\Tools\toolboxes\TrialDataPrep.pyt", line 76, in PointOverlap
File "C:\Users\patri\bitbucket\in10t-gp-tools\Tools\toolboxes\TrialDataPrep.pyt", line 73, in PointOverlap
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\geoprocessing\_base.py", line 511, in <lambda>
return lambda *args: val(*gp_fixargs(args, True))
arcgisscripting.ExecuteError: Traceback (most recent call last):
File "C:\Users\patri\bitbucket\in10t-gp-tools\Tools\scripts\PointOverlapToolClasses.py", line 158, in execute
arcpy.CopyFeatures_management(merge_out, outarea)
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\management.py", line 3232, in CopyFeatures
raise e
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\management.py", line 3229, in CopyFeatures
retval = convertArcObjectToPythonObject(gp.CopyFeatures_management(*gp_fixargs((in_features, out_feature_class, config_keyword, spatial_grid_1, spatial_grid_2, spatial_grid_3), True)))
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\geoprocessing\_base.py", line 511, in <lambda>
return lambda *args: val(*gp_fixargs(args, True))
arcgisscripting.ExecuteError: ERROR 000210: Cannot create output Jason_Diekevers_19CM_Corn_corridors_asapplied_flow1
Failed to execute (CopyFeatures).

Note the bold print above, the full path is not being passed to the tool.

Thanks for any help.

0 Kudos
4 Replies
DuncanHornby
MVP Notable Contributor

No one is going to answer this question until you format your code as it is difficult to read un-formatted python. You should always format your code as discussed here.

0 Kudos
PatrickHill
New Contributor II
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."""
        m = 'updating parameters'
        arcpy.AddMessage(m)

        if parameters[0].value is not None and parameters[1].value is not None:
            name = self.get_name(parameters[0].valueAsText, parameters[1].valueAsText)
            parameters[4].value = name
            corridor_name = self.get_corridor_name(parameters[0].valueAsText, parameters[1].valueAsText)
            parameters[3].value = corridor_name

def get_name(self, planting_path, as_applied_path):
        planting_base = os.path.basename(planting_path)
        as_applied_base = os.path.basename(as_applied_path)
        split = planting_base.split('_')
        prefix = '{}_{}'.format(split[0], split[1])
        operation = '{}_{}'.format(split[2], split[3])
        po = '{}_{}'.format(prefix, operation)
        as_applied = as_applied_base.replace(po, "")
        name = '{}{}{}'.format(po, '_planting_modified', as_applied)
        return name

def get_corridor_name(self, planting_path, as_applied_path):
        planting_base = os.path.basename(planting_path)
        as_applied_base = os.path.basename(as_applied_path)
        split = planting_base.split('_')
        prefix = '{}_{}'.format(split[0], split[1])
        operation = '{}_{}'.format(split[2], split[3])
        po = '{}_{}'.format(prefix, operation)
        as_applied = as_applied_base.replace(po, "")
        name = '{}{}{}'.format(po, '_corridors', as_applied)
        m = 'test name validation: {}'.format(name)
        arcpy.AddMessage(m)
        return name‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍


class TestFindPointOverlapTestCase(unittest.TestCase, arcpyAssert.FeatureClassAssertMixin):

   @classmethod
   def setUpClass(self):
      try:
         toolboxdir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'Tools')
         self.toolbox_under_test = os.path.join(toolboxdir, 'toolboxes', 'TrialDataPrep.pyt')
         self.extract_to = testutils.get_temp_folder()
         zip_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'testdata.zip')
         input_gdb = testutils.extract_zipped_gdb(zip_path, self.extract_to, 'FindPointOverlapData.gdb')
         self.input_target = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_Planting')
         self.input_compare = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_asapplied_flow1')
         self.target_id_field = 'Trial_0_1'
         output_gdb = testutils.create_output_gdb(self.extract_to)
   

         self.output_features = os.path.join(output_gdb, 'test_points') #this path overwritten by updateParameters()

         self.output_areas = os.path.join(output_gdb, 'test_areas') #this path overwritten by updateParameters()


         self.compare_pts = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_planting_modified_asapplied_flow1')
         self.compare_areas = os.path.join(input_gdb, 'Jason_Diekevers_19CM_Corn_corridors_asapplied_flow1')
      except Exception:
         super(TestFindPointOverlapTestCase, self).tearDownClass()

  

   @classmethod
   def tearDownClass(self):
      testutils.delete_output_gdb(self.extract_to)

 

   def testFindPointOverlap(self):
      arcpy.ImportToolbox(self.toolbox_under_test)
      print('output_areas = ' + self.output_areas)
      arcpy.PointOverlap_trials(self.input_target, self.input_compare, self.target_id_field, self.output_areas,                                              self.output_features)
PatrickHill
New Contributor II

Updated the code for better formatting.

So still wondering when 'updateParameters()' is called? I thought it only ran when a dialog value was changed but it runs prior to execute when running from script. This causes my test to fail because it does not append the directory to my parameter output value when I run from script. Since the full path is not accessed with the output parameter the tool cannot create the output feature class. When running from pro the directory path is implied and only the name of the output feature class is required when setting the output value. In pro the tool works as expected. If I remove the code where I set the output parameter name in updateParameters() the test runs without error. I really need to define those output parameter names, however, since we are using a pretty strict naming convention for output feature classes.

0 Kudos
JoshuaKnight
New Contributor

Yes we discovcered the same issue, which is annoying when they tell you to set defaults here -

https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/customizing-script-tool-beha...

 

What we noticed is the ArcGIs default tools, only set the default output filename for the first file selected i.e. when the output filename is empty.

So we simply added a simply check to only set default output filenames, if the output file value is empty.

0 Kudos