4

I created a small arcpy script tool which works fine when I run it from ArcMap. All it does is list the fields from a specified layer.

import arcpy
myLayer = arcpy.GetParameter(0)
fieldList = arcpy.ListFields(myLayer)
for field in fieldList:
    arcpy.AddMessage(field.name)

However if I create a Python Toolbox with this same script, it fails on fieldList = arcpy.ListFields(myLayer) with this error:

Traceback (most recent call last):
  File "<string>", line 37, in execute
  File "c:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\__init__.py", line 1131, in ListFields
    return gp.listFields(dataset, wild_card, field_type)
  File "c:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\_base.py", line 344, in listFields
    self._gp.ListFields(*gp_fixargs(args, True)))
IOError: "" does not exist

Failed to execute (TestPYTListFields).

If I replace fieldList = arcpy.ListFields(myLayer) with fieldList = arcpy.Describe(myLayer).fields it works as expected.

Why does my ListFields() fail from a Python Toolbox, and what can I put in place to make it work?

I suspect it may be something to do with the Feature Layer object, as I'm also having Search Cursors fail when referencing the Feature layer objects as well, although I haven't tested that to the same extent as the ListFields.

Below is my entire PYT:

import arcpy

class Toolbox(object):
    def __init__(self):
        self.label = u'ListFields'
        self.alias = ''
        self.tools = [TestPYTListFields]

class TestPYTListFields(object):
    def __init__(self):
        self.label = u'TestPYTListFields'
        self.description = u''
        self.canRunInBackground = False

    def getParameterInfo(self):
        param_1 = arcpy.Parameter()
        param_1.name = u'Input_Layer'
        param_1.displayName = u'Input Layer'
        param_1.parameterType = 'Required'
        param_1.direction = 'Input'
        param_1.datatype = u'Feature Layer'
        return [param_1]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        import arcpy
        myLayer = parameters[0]

        fieldList = arcpy.ListFields(myLayer)         # ListFields doesn't work from PYT
        #fieldList = arcpy.Describe(myLayer).fields   # Describe().fields does work from PYT

        for field in fieldList:
            messages.AddMessage(field.name)
        return
4

When I tested your script, it returns myLayer as geoprocessing parameter object object at... and its uninterpreted value is a string, therefore your IOError (i.e., ListField expects a file location or a layer referencing to a data source, which is bound to a file as well).

If you change your ListField syntax to fieldList = arcpy.ListFields(myLayer.value), it will work fine. I think the issue is similar to interpretation of feature layers in your mxd as string in Python interpreter.

Any tool that you run in ArcGIS accepts layer names in your TOC as string, for example, arcpy.RepairGeometry_management("aaaaaa_a") runs perfectly, but "aaaaaa_a".dataSource (which one may expect to be interpreted as a reference to layer from the previous logic) will throw AttributeError: 'str' object has no attribute 'dataSource'.

  • While I agree with you for most of it, I should be receiving a layer object, rather than a string with a layer name, due to the myLayer = parameters[0] rather than myLayer = parameters[0].valueAsText. This should allow me to use the layer object for further tools (as in the original script tool) without having to make feature layer. I'll have a play with what you've suggested and report back – Midavalo Aug 15 '16 at 23:31
  • 1
    My experience is that assigning a Feature Layer as a parameter to a tool/script is one of the trickiest processes in this domain. That's why I hate working with arcpy.MakeFeatureLayer_management and prefer arcpy.mapping.Layer as far as I can. This may look distant to your question but I think highly relevant. – fatih_dur Aug 15 '16 at 23:35
  • 1
    myLayer.value does appear to be the answer - this is making my Search Cursor work in my other toolbox as well, yet it still appears to be treating it as the layer object so I don't need to make feature layer. Many thanks! – Midavalo Aug 15 '16 at 23:40
  • 1
    @Midavalo "I should be receiving a layer object, rather than a string with a layer name, due to the myLayer = parameters[0]" this will give you a parameter object, not a layer object, which is why parameters[0].value works. – user2856 Aug 16 '16 at 1:01
  • @Luke thanks for the explanation, that makes sense – Midavalo Aug 16 '16 at 1:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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