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.
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.
Hey @DMac
It appears that one of your iterables in thisFD are None, so we would have to get around that, optimally reporting the error as well, here is something to try:
if thisFD = None:
print('thisFD is None, skipping')
else:
for FCs in thisFD:
{rest of your code}
I can't really tell where your indentation should start and end, the formatting seems to have not came through, so everything that should be within for FCs in thisFD should be contained in {rest of your code}.
Hi there,
It would help to see your code in the code-style font/format. You can do that by clicking the button to expand the toolbar (when you are writing a reply here), then click the </> icon to insert code.
I see that you are using the variable "thisFD" for arcpy.ListFeatureClasses(). For me, this variable name could cause a little confusion and obscure a possible reason for your error. I would expect "thisFD" to represent the feature dataset name or object, so I would not expect it to end up as None, because how would you have a feature dataset name for a nonexistent feature dataset? But since the variable is representing the list of feature classes within the dataset, you could have a feature dataset with nothing in it. So the variable could represent None (no feature classes). That's where I find it could cause confusion. Personally, I find it helpful to use variable names that very literally indicate what they represent. I think if the variable name was something like "fc_list", it would very clearly indicate what it represents, and it would indicate that it could be empty/None, because a list can be empty. I agree with what the previous person said, to add handling to deal with an empty list.
# 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)
Above is the code re-displayed in the correct format. Sorry for the newbie mistake!
Hey @DMac
This is how I would reformat it to check for these errors:
# 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
arcpy.AddMessage("Current FDs value: {}").format(FDs)
thisFD = arcpy.ListFeatureClasses()
arcpy.AddMessage("Current thisFD value: {}").format(thisFD)
if thisFD == None:
arcpy.AddMessage("Error encountered, thisFD is None")
else:
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)
In this, I've added a few print statements to record the current FDs and thisFD values, this should clear up when the program is causing the error, such as the supporting error message, but that will now be avoided.
Cody
Hey @CodyPatterson thanks for the reply! When I run it with your changes I get this error "Traceback (most recent call last): File "D:\Users\dmckay\Documents\FeatureCountAutomation_OG_Test\CODE\FeatureCount9UtilFDs_R1.py", line 28, in <module>
arcpy.AddMessage("Current FDs value: {}").format(FDs)
AttributeError: 'NoneType' object has no attribute 'format'"
This NoneType error is confusing me because I know there is data inside the datasets that I am choosing but my code must be wrong somewhere.
Hey @DMac
That is my bad, instead, use f strings:
# 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
index = 0
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
arcpy.AddMessage(f"Current FDs value: {FDs} index: {index}")
thisFD = arcpy.ListFeatureClasses()
arcpy.AddMessage(f"Current thisFD value: {thisFD} index: {index}")
if thisFD == None:
arcpy.AddMessage(f"Error encountered, thisFD is None index: {index}")
else:
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")
index += 1
#Close the output file
arcpy.SetParameter(1, outfile)
The reason there is Nonetype may be due to an access issue in the layer, maybe there are two or more maps to pull from, give this a shot and lets see where the issue would be at.
Cody
The code ran. Seems like it isn't able to see any of the data. Here is the message:
"Current FDs value: D:\Users\dmckay\Documents\FeatureCountAutomation_New\GeodatabaseForCodeTesting.gdb\Telecommunication index: 0
Current thisFD value: None index: 0
Error encountered, thisFD is None index: 0
Current FDs value: D:\Users\dmckay\Documents\FeatureCountAutomation_New\GeodatabaseForCodeTesting.gdb\Utilities_Electrical index: 1
Current thisFD value: None index: 1
Error encountered, thisFD is None index: 1"
I'm confused as to why it would be returning a None at all? It should be returning an empty list.
See: I test on an empty feature dataset.
Actually.
The reason you're having a problem is this:
gdbpath + "/" + FDs
It's not joining the GDB path and the feature dataset name, it's joining the gdb path and feature dataset path.
C:\Users\local_abaldenweck\Temp\ArcGISProTemp9892\Untitled\Default.gdb/C:\Users\local_abaldenweck\Temp\ArcGISProTemp9892\Untitled\Default.gdb\testFD
Your UtilsClass is returning the full path of the Dataset. I'm not 100% sure what you're doing here, but you need to either use the FDs variable as the workspace environment. (Or maybe replace part of the path with the working gdb in the first parameter? idk)
Also, it will be less headache for you to use the os module for path manipulations in the future.
fdPath = os.path.join(gdb, fdname)
is cleaner than
fdPath = gdb + "\\" + fdname
Also if you want to do a replace for the path in this case, I'd use
# Get the name of the file or folder or featuredataset
fdname = os.path.basename(full fd path)
fdPath = os.path.join(gdb,fdname)
I hope this helps!