Select to view content in your preferred language

arcpy.List[Type] functions: Let us feed it a workspace instead of having to switch the environment

149
4
Thursday
AlfredBaldenweck
MVP Regular Contributor

The workspace environment must be set before using several of the list functions, including ListDatasetsListFeatureClassesListFilesListRastersListTables, and ListWorkspaces.

It is super, super annoying to use the arcpy.ListFeatureClasses, ...Tables, etc. functions right now because you can't specify a workspace you're checking.

Instead, you have to set the environment to that workspace, first.

arcpy.env.workspace = "whatever.gdb"
for tab in arcpy.ListTables():
print(tab.name)

I would love, love, LOVE if we didn't have to do all that and could just feed it the workspace directly.

Right now, the call for List FeatureClasses, (for example) is as follows :

ListFeatureClasses ({wild_card}, {feature_type}, {feature_dataset})

 Could we please change it to 

 

 

ListFeatureClasses ({wild_card}, 
                    {feature_type}, 
                    {feature_dataset}, 
                    {workspace = arcpy.env.workspace}
)

 

 

 

That is, make it go to the environment workspace by default, unless you specify a different workspace?

I just want to be able to get a list of all the stuff for each gdb in a list without having to change the environment all the time.

4 Comments
ShaunWalbridge
Status changed to: Needs Clarification

Thanks for the idea!

From what I understand you're trying to do, the existing alternative approaches would be to use envManager to operate multiple calls against a specific GDB:

arcpy.env.workspace = "whatever.gdb"
# find tables only in 'other.gdb':
with arcpy.EnvManager("workspace": "other.gdb"):
    for table in arcpy.ListTables():
        print(table.name)

# reset to the global state, now get 'whatever.gdb' results
for table in arcpy.ListTables():
    print(table.name)


The other approach you could take if you have a large collection of geodatabases to search is to use arcpy.da.Walk instead. In that model, you'd point to some place higher up in your heirarchy that includes multiple geodatabases, then filter them as you go through the walk iterations:

 

import arcpy
import os

workspace = r"C:\data"
feature_classes = {}

# can change datatype as needed to filter separate groups
walk = arcpy.da.Walk(workspace, datatype="FeatureClass")

for dirpath, dirnames, filenames in walk:
    for filename in filenames:
        if dirnames:
            last_dirname = dirnames.split(os.sep)[-1]
            if last_dirname.endswith('.gdb'):
                if last_dirname not in feature_clases:
                    feature_classes[last_dirname] = []

                feature_classes[last_dirname].append(filename)

 


Given that there are a few well supported existing paths for this, I think it would be better to try and adopt those patterns rather than the functions changing their relationship with workspaces.

BlakeTerhune

I would support the idea proposed by @AlfredBaldenweck.

I think the idea is not to propose functionality that isn't possible, but rather to have the list functions follow a similar pattern to most other functions and have a parameter to explicitly set the workspace. It feels odd that you need to use arcpy.env.workspace (primarily) only when dealing with a list function, but not for most other things.

AlfredBaldenweck

Yeah, Blake has the right idea.

For example, ListFields takes the table that we're querying as a parameter. Why don't the other list functions do that?

I'm very sure that it's possible to do like

import arcpy
fgdbList = ["gdb1.gdb", "gdb2.gdb"]
for fgdb in fdbList:
    with arcpy.EnvManager(workspace=fgdb):
        feature_classes = arcpy.ListFeatureClasses(feature_type='POLYGON')

 

But why can't we just do the much more intuitive:

import arcpy
fgdbList = ["gdb1.gdb", "gdb2.gdb"]
for fgdb in fdbList:
    feature_classes = arcpy.ListFeatureClasses(feature_type='POLYGON', 
                                               workspace = fgdb
                                              )

 

DavidSolari

Following the usual precedence for "old function but good" these would be Data Access functions, say:

 

my_workspace = r"C:\path\to\data.gdb"
my_dataset = "ImportantItems"

# Get all feature classes as some sort of proper object, including any dataset objects
all_fcs = [f.name for f in arcpy.da.ListFeatureClasses(my_workspace)]

# Now just the dataset
dataset_fcs = [f.featureType for f in arcpy.da.ListFeatureClasses(f"{my_workspace}\\{my_dataset}")]
alternatively = [f.featureType for f in arcpy.da.ListFeatureClasses(my_workspace, feature_datasets=[my_dataset])]

# Why call many functions when you can call one?
everything = arcpy.da.ListObjects(my_workspace)

# But some database-side filtering is good for large workspaces
special_photos = arcpy.da.ListRasters(my_workspace, "county_*")
special_lines = arcpy.da.ListFeatureClasses(my_workspace, "river_*", "POLYLINE")

 

You get the idea, lots of room for more feature-rich, less stateful functions that play better with all those new typestubs.