7a55a823e6fe9bc65ac0ecf5b6576e90fda64bcb
hiram
  Wed Aug 12 16:10:27 2026 -0700
fixups per code review refs #38005

diff --git src/hg/utils/automation/gffAttrsToIx.py src/hg/utils/automation/gffAttrsToIx.py
index 27c074d21e6..1e590bf7c22 100755
--- src/hg/utils/automation/gffAttrsToIx.py
+++ src/hg/utils/automation/gffAttrsToIx.py
@@ -14,30 +14,31 @@
 geneAttrs.ncbi.txt): when gff3ToGenePred is run with -useName, the
 attribute rows for an mRNA record are written keyed by the *resolved*
 genePred item name (e.g. "GCSAML"), not the raw GFF3 "rna-..." id.  That
 means the first column of attrsOut already matches the genePred name
 field directly -- no id-juggling needed to join transcript attributes.
 The gene-level block, by contrast, stays keyed by the raw "gene-..." id,
 and is reached from the transcript block via its "Parent" attribute.
 
 usage:
    gffAttrsToIx.py asmId.geneAttrs.ncbi.txt asmId.ncbiGene.genePred.gz \
        | sort -u > extra.ix.txt
 """
 
 import sys
 import gzip
+import re
 from collections import defaultdict
 
 # GFF3/attrsOut attribute names worth surfacing as search terms, and
 # whether they come off the transcript (rna-) record or the gene record.
 TX_ATTRS = ("product", "Note", "gene")
 GENE_ATTRS = ("Name", "description", "gene", "gene_synonym", "locus_tag")
 
 # Dbxref source prefixes worth stripping so the bare id becomes searchable
 # (same list gff3ToGenePred/ncbiRefSeqOtherAttrs.pl already know about).
 DBXREF_SOURCES = ("GeneID", "MIM", "HGNC", "MGI", "WormBase", "XenBase",
                    "BGD", "RGD", "SGD", "ZFIN", "FlyBase", "miRBase",
                    "NCBIOrtholog")
 
 
 def openMaybeGz(path):
@@ -53,56 +54,56 @@
                 continue
             parts = line.rstrip("\n").split("\t")
             if len(parts) < 3:
                 continue
             itemId, attr, val = parts[0], parts[1], parts[2]
             attrs[itemId][attr].append(val)
     return attrs
 
 
 def genePredNames(gpFile):
     """distinct values from column 1 (name) of the genePred"""
     names = []
     seen = set()
     with openMaybeGz(gpFile) as fh:
         for line in fh:
-            if line.startswith("#"):
+            line = line.strip()
+            if not line or line.startswith("#"):
                 continue
             name = line.split(None, 1)[0]
             if name not in seen:
                 seen.add(name)
                 names.append(name)
     return names
 
 
 def dbxrefTerms(values):
     terms = []
     for val in values:
         for xref in val.split(","):
             bare = xref
             for source in DBXREF_SOURCES:
                 prefix = source + ":"
                 if xref.lower().startswith(prefix.lower()):
                     bare = xref[len(prefix):]
                     break
             terms.append(bare)
     return terms
 
 
 def noSuffix(name):
     """strip a trailing .NNN version suffix, same trick as gpToIx.pl"""
-    import re
     stripped = re.sub(r"\.[0-9]+$", "", name)
     return stripped if stripped != name else None
 
 
 def buildAliases(name, attrs):
     txRec = attrs.get(name, {})
     terms = []
 
     for attr in TX_ATTRS:
         terms.extend(txRec.get(attr, []))
     terms.extend(dbxrefTerms(txRec.get("Dbxref", [])))
 
     for geneId in txRec.get("Parent", []):
         geneRec = attrs.get(geneId, {})
         for attr in GENE_ATTRS: