fix: support gazelle generation_mode:update_only (#2708)

This just fixes a crash when `generation_mode: update_only` causes
`GenerateRules` to not be invoked for 100% of directories.

Fix #2707
diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go
index 2183ec6..23c0cfd 100644
--- a/gazelle/pythonconfig/pythonconfig.go
+++ b/gazelle/pythonconfig/pythonconfig.go
@@ -22,8 +22,8 @@
 
 	"github.com/emirpasic/gods/lists/singlylinkedlist"
 
-	"github.com/bazelbuild/bazel-gazelle/label"
 	"github.com/bazel-contrib/rules_python/gazelle/manifest"
+	"github.com/bazelbuild/bazel-gazelle/label"
 )
 
 // Directives
@@ -125,21 +125,28 @@
 
 // defaultIgnoreFiles is the list of default values used in the
 // python_ignore_files option.
-var defaultIgnoreFiles = map[string]struct{}{
-}
+var defaultIgnoreFiles = map[string]struct{}{}
 
 // Configs is an extension of map[string]*Config. It provides finding methods
 // on top of the mapping.
 type Configs map[string]*Config
 
 // ParentForPackage returns the parent Config for the given Bazel package.
-func (c *Configs) ParentForPackage(pkg string) *Config {
-	dir := path.Dir(pkg)
-	if dir == "." {
-		dir = ""
+func (c Configs) ParentForPackage(pkg string) *Config {
+	for {
+		dir := path.Dir(pkg)
+		if dir == "." {
+			dir = ""
+		}
+		parent := (map[string]*Config)(c)[dir]
+		if parent != nil {
+			return parent
+		}
+		if dir == "" {
+			return nil
+		}
+		pkg = dir
 	}
-	parent := (map[string]*Config)(*c)[dir]
-	return parent
 }
 
 // Config represents a config extension for a specific Bazel package.
diff --git a/gazelle/pythonconfig/pythonconfig_test.go b/gazelle/pythonconfig/pythonconfig_test.go
index 7cdb9af..fe21ce2 100644
--- a/gazelle/pythonconfig/pythonconfig_test.go
+++ b/gazelle/pythonconfig/pythonconfig_test.go
@@ -248,3 +248,35 @@
 		})
 	}
 }
+
+func TestConfigsMap(t *testing.T) {
+	t.Run("only root", func(t *testing.T) {
+		configs := Configs{"": New("root/dir", "")}
+
+		if configs.ParentForPackage("") == nil {
+			t.Fatal("expected non-nil for root config")
+		}
+
+		if configs.ParentForPackage("a/b/c") != configs[""] {
+			t.Fatal("expected root for subpackage")
+		}
+	})
+
+	t.Run("sparse child configs", func(t *testing.T) {
+		configs := Configs{"": New("root/dir", "")}
+		configs["a"] = configs[""].NewChild()
+		configs["a/b/c"] = configs["a"].NewChild()
+
+		if configs.ParentForPackage("a/b/c/d") != configs["a/b/c"] {
+			t.Fatal("child should match direct parent")
+		}
+
+		if configs.ParentForPackage("a/b/c/d/e") != configs["a/b/c"] {
+			t.Fatal("grandchild should match first parant")
+		}
+
+		if configs.ParentForPackage("other/root/path") != configs[""] {
+			t.Fatal("non-configured subpackage should match root")
+		}
+	})
+}