Hey everyone! I am a python novice and am having trouble with a script I'm trying to develop which will then be put into a toolbox. I'm creating the script with the Python IDLE version 3.9.18 and running it in ArcPro 3.2.
The basics of the tool are it should take in the user parameters and spit out a .csv file with the counts of features for each feature class in each selected feature dataset.
The tool has 3 user parameters:
1. Geodatabase - Geodatabase where data is located
2. Feature Datasets - Selected feature datasets from the above geodatabase
3. Output location - Location for output .csv file
The output of the tool should be a .csv file that has 3 columns: DataSet (name of dataset), FeatureClass(name of feature class), and NumFeatures(number of features in the feature class)
When I try to run the below script it gives me an error " Traceback (most recent call last): File "D:\Users\dmckay\Documents\FeatureCountAutomation_OG_Test\CODE\FeatureCount9UtilFDs_R1.py", line 29, in <module>
for FCs in thisFD:
TypeError: 'NoneType' object is not iterable"
Here is the script. Any help is appreciated!
# Custom script to extract feature counts for each featureclass in a hard coded list of feature data sets
# Output = CSV
import arcpy
# Get production workspace path from first user prompt
gdbpath = arcpy.GetParameterAsText (0)
arcpy.env.workspace = gdbpath.replace("\\", "/")
arcpy.env.overwriteOutput = True
# List all feature datasets in the geodatabase
feature_datasets = arcpy.ListDatasets(feature_type='Feature')
# Get the user-selected feature datasets (multi-value parameter) - Second user prompt
UtilClass = arcpy.GetParameterAsText(1).split(";")
# Get output file path and name from second user prompt
outfile_path = arcpy.GetParameterAsText(2)
# Open the output file in write mode - Python
with open(outfile_path, "w") as outfile:
# Write column headings
outfile.write("DataSet,FeatureClass,NumFeatures\n")
for FDs in UtilClass:
# Update workspace path for each feature dataset
arcpy.env.workspace = gdbpath + "/" + FDs
thisFD = arcpy.ListFeatureClasses()
for FCs in thisFD:
if "." in FCs:
FCs = FCs.split(".")
FCs = FCs[len(FCs)-1]
arcpy.AddMessage("Working on {0}: {1}".format(FDs, FCs))
# Write Feature Dataset Name
outfile.write(format(FDs))
# Write Field Delimiter
outfile.write(",")
# Write Featureclass Name
outfile.write(format(FCs))
# Write Field Delimiter
outfile.write(",")
# Write Feature Count
outfile.write(format(arcpy.GetCount_management(FCs)))
# Write New Line
outfile.write("\n")
#Close the output file
arcpy.SetParameter(1, outfile)
Solved! Go to Solution.
Hey @AlfredBaldenweck thanks for the reply. After doing some testing I think what's confusing me is the arcpy.ListDatasets() function. When I run a test on this just in the arcpy window I get the same NoneType error.
But when I set the workspace to one of the feature datasets and run the arcpy.ListFeatureClasses I get back a list of all the feature classes.
Is there something about the ListDatasets function that I don't get? Shouldn't it list the datasets in the geodatabase workspace?
It does.
(BTW I don't think you actually do anything with you feature_datasets variable, so idk why it's there)
The thing with the list functions (ListDatasets, ListFeatureClasses) is that it relies on your workspace. That is, you can't feed it a geodatabase and have it figure it out; you have to set the workspace environment first.
For reasons I do not understand or agree with, a feature dataset is considered a workspace. So, in this example, I have a file gdb with a feature dataset containing one feature class and nothing else.
If you're feeding it a path that exists, both ListDatasets() and ListFeatureClasses() should return a list, even if it's empty (see part one of the second picture). Like I said, I think your issue is that you're feeding it a bad path, and when you manually do it Pro you're explicitly feeding it a good path.
Yes I understand that. I think the problem lies in the location of my workspace. I'm trying to run this on a versioned SDE and I'm wondering if the path I'm using isn't the "actual" path but some kind of shortcut for lack of a better word.
Still seems weird that I can list the feature classes using the SDE datasets as the workspace but I can't list the datasets using the geodatabase as the workspace.
I was able to run the List functions on the datasets using a regular file geodatabase so the issue must be with my sde connection. Thanks for the help!
hmmmm, weird.
I did the same thing on an SDE file (connected to default; I'm having issues editing my SDE files to a specific version today) and didn't get the error:
(This eGDB doesn't have any feature classes outside of a feature dataset)
Update yeah I connected to a specific version with that SDE file and it worked fine.
Also, reviewing the documentation:ListFeatureClasses—ArcGIS Pro | Documentation
If you know the feature dataset you want, it appears you can feed that to ListFeatureClasses() without having to change the environment?
This looks interesting. Thanks @AlfredBaldenweck !
I figured I'd use this as way to not only fix your script tool, but demonstrate some common newbie Python mistakes and a way to fix them. This isn't a condemnation of your skills or anything, I too was a new programmer once! Anyways:
import arcpy
from os import path
from csv import writer
from typing import Iterable, Iterator
def get_rows(dataset: str) -> Iterator[tuple[str, str, str, str]]:
"""
Yield the workspace name, dataset name, feature class name and feature count for
every feature class in a dataset.
"""
workspace, dataset_name = path.split(dataset)
# EnvManager doesn't work, save and restore workspace like this
old_workspace = arcpy.env.workspace
arcpy.env.workspace = workspace
for feature_class in arcpy.ListFeatureClasses(feature_dataset=dataset_name):
name = feature_class.split(".")[-1]
count = arcpy.management.GetCount(feature_class)[0]
yield workspace, dataset_name, name, count
arcpy.env.workspace = old_workspace
def main(datasets: Iterable[str], output: str):
"""
Write a table of feature counts from the selected datasets.
"""
with open(output, "w", newline="") as f:
csv_writer = writer(f)
csv_writer.writerow(("Geodatabase", "DataSet", "FeatureClass", "NumFeatures"))
for dataset in datasets:
for row in get_rows(dataset):
csv_writer.writerow(row)
def execute(params: list[arcpy.Parameter]):
datasets = params[0].valueAsText.split(";")
output = params[1].valueAsText
main(datasets, output)
if __name__ == "__main__":
execute(arcpy.GetParameterInfo())
Now for the notes:
Whoops, wrote a whole essay! Hope this provided some guidance for your future work, if anyone takes umbrage with these tips please reply, I'm sure I'm making some mistakes of my own.
Thanks @DavidSolari !! This really helped and made it easier for me to understand. I appreciate your explanations, they make sense and now I can use them for future projects. Thanks again!