arcpy layer.dataSource property contains exception

1427
4
Jump to solution
01-05-2022 04:03 PM
davedoesgis
Occasional Contributor III

I am trying to loop through the layers in my APRX and print out their data source. When I get to an anno layer, everything looks good, but the next layer is a subclass of that anno layer and gives me an error. Imagine I am looping through a list of layers like this: 

 

for m in aprx.listMaps():
    for lyr in m.listLayers():
        print(lyr.name)
        print(lyr.dataSource)

 

For an anno class layer, I am getting this NameError exception on the last line. 

Traceback (most recent call last):
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\_base.py", line 90, in _get
return convertArcObjectToPythonObject(getattr(self._arc_object, attr_name))
AttributeError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\eclipse\plugins\org.python.pydev.core_7.6.0.202006041357\pysrc\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<console>", line 1, in <module>
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\_base.py", line 96, in _get
(attr_name, self.__class__.__name__))
NameError: The attribute 'dataSource' is not supported on this instance of Layer.

If I put my IDE into debug mode and stop on a breakpoint, I'm seeing some really odd stuff. The dataSource is there, and is of type str. The value of the property is exactly the Traceback message I see at the console. I've never seen an object's property have an exception locked and loaded this way (before I even try to access it), and I am super puzzled at the property being type str.  Any attempt to use the property, like print(lyr.dataSource), raises the NameError exception. I even get the exception when I try to test for it: 

 

hasattr(lyr, 'dataSource')

 

NOTE: There are several other str properties in this object with this exception information in them, such as brightness, connectionProperties, and definitionQuery.   

 

QUESTIONS: 

1. What is the best way to solve this? I can test for this using a try/except block and trap NameError. That works, but it feels like forcing a failure and catching it is not Pythonic. Is there a better way? 

2. Looking beyond solving the problem, I'd like to learn how a class's property can store an exception like this. I tried to create my own class with a property containing the exact same str value. Python just treats that property like regular text and does not raise a NameError exception when I work with that property. I personally don't think what Esri is doing here is Pythonic, and suspect it isn't even intentional, but I am curious to know how one puts an exception into a property when it is instantiated. I can't think of a use case for this, but I'm always down to learn more Python. 

 

 

0 Kudos
1 Solution

Accepted Solutions
DanPatterson
MVP Esteemed Contributor

not all layer types support all properties, you have to follow the trail

Layer—ArcGIS Pro | Documentation

and test for it

supports (layer_property)

Specifically

DATASOURCE—A layer's file path or connection file.

So sometimes you need to test the layer to see if it supports property you want to get/set first 


... sort of retired...

View solution in original post

4 Replies
DanPatterson
MVP Esteemed Contributor

not all layer types support all properties, you have to follow the trail

Layer—ArcGIS Pro | Documentation

and test for it

supports (layer_property)

Specifically

DATASOURCE—A layer's file path or connection file.

So sometimes you need to test the layer to see if it supports property you want to get/set first 


... sort of retired...
davedoesgis
Occasional Contributor III

Thanks, that should work, and is definitely cleaner than a try/catch. Still feels like the best way to model this is to create inherited classes for feature, raster, and other layer types that only have relevant properties. Just seems like it's too easy to write valid code that compiles and get an exception, but I'm probably shouting into the wind on this. 

Semi-related - what's the best way to get the layer type for a layer in an APRX map? There's a whole bunch of fields for isFeatureLayer, isRasterLayer, isBasemapLayer, etc., but is there a simple way to get a property for the layer type without playing 20 questions? thanks!

0 Kudos
DanPatterson
MVP Esteemed Contributor

the CIM is evolving based on user needs, but it won't be a replacement for Pro's user interface anytime soon


... sort of retired...
0 Kudos
davedoesgis
Occasional Contributor III

Dan's suggestion of using the supports('<layer_property>') check is the most correct way to do this. It still feels like the most Pythonic implementation would have been for each type of layer to have its own class, but that would create much more introspection and checking than the current approach. So score one for pragmatic solutions over Pythonic solutions. 

NOTE: There is at least one property I've discovered that does not accurately work with the supports() function: the showLabels property. 

When a layer supports labels and labels are turned on, it works as expected: 

 

>>> lyr.supports('showLabels')
True
>>> lyr.showLabels
True

 

 

When a layer does not support labels, it also works as expected, raising a NameError exception if you try to use that property: 

 

>>> lyr.supports('showLabels')
False
>>> lyr.showLabels  # THIS SHOULD RAISE AN EXCEPTION, AND IT DOES
Traceback (most recent call last):
  File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\_base.py", line 90, in _get
    return convertArcObjectToPythonObject(getattr(self._arc_object, attr_name))
AttributeError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\eclipse\plugins\org.python.pydev.core_7.6.0.202006041357\pysrc\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<console>", line 1, in <module>
  File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\_base.py", line 96, in _get
    (attr_name, self.__class__.__name__))
NameError: The attribute 'showLabels' is not supported on this instance of Layer.

 

 

BUT, if a layer supports labeling, but just happens to have the labels turned off, it returns False for the supports() function: 

 

>>> # THIS SHOULD BE TRUE; THE LAYER SUPPORTS LABELS, EVEN THOUGH THEY ARE OFF
>>> lyr.supports('showLabels')  
False
>>> # BUT NOW THAT supports() IS FALSE, THIS SHOULD RAISE AN EXCEPTION
>>> lyr.showLabels 
False

 

This may seem like a trivial thing, but it means I still have to put my code in a try/except block and force an exception to find out if showLabels is supported. 

Regarding my question #2, my best guess is that Esri is not pre-loading those exceptions into the properties. The more I think about it, my Python IDE must be trying to ping all the properties to fill in their variables, and so the API returns those exceptions at the moment my IDE enters debug mode. I guess my IDE stringifies that NameError, leading to what I see. 

 

0 Kudos