fix(gazelle): make cmd.Wait more idiomatic (#1550)
It seems that the documentation for the `cmd.Wait` explicitly
asks the users to not wait on the command immediately after
starting because it may close pipes too early and cause
unintended side-effects as described in #1546.
Fixes #1546.
Co-authored-by: Richard Levasseur <rlevasseur@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6b419d..32ab939 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -97,6 +97,9 @@
* (gazelle) Generate a single `py_test` target when `gazelle:python_generation_mode project`
is used.
+* (gazelle) Move waiting for the Python interpreter process to exit to the shutdown hook
+ to make the usage of the `exec.Command` more idiomatic.
+
* (toolchains) Keep tcl subdirectory in Windows build of hermetic interpreter.
* (bzlmod) sub-modules now don't have the `//conditions:default` clause in the
diff --git a/gazelle/python/parser.go b/gazelle/python/parser.go
index ad55e03..8931026 100644
--- a/gazelle/python/parser.go
+++ b/gazelle/python/parser.go
@@ -32,6 +32,7 @@
)
var (
+ parserCmd *exec.Cmd
parserStdin io.WriteCloser
parserStdout io.Reader
parserMutex sync.Mutex
@@ -40,40 +41,37 @@
func startParserProcess(ctx context.Context) {
// due to #691, we need a system interpreter to boostrap, part of which is
// to locate the hermetic interpreter.
- cmd := exec.CommandContext(ctx, "python3", helperPath, "parse")
- cmd.Stderr = os.Stderr
+ parserCmd = exec.CommandContext(ctx, "python3", helperPath, "parse")
+ parserCmd.Stderr = os.Stderr
- stdin, err := cmd.StdinPipe()
+ stdin, err := parserCmd.StdinPipe()
if err != nil {
log.Printf("failed to initialize parser: %v\n", err)
os.Exit(1)
}
parserStdin = stdin
- stdout, err := cmd.StdoutPipe()
+ stdout, err := parserCmd.StdoutPipe()
if err != nil {
log.Printf("failed to initialize parser: %v\n", err)
os.Exit(1)
}
parserStdout = stdout
- if err := cmd.Start(); err != nil {
+ if err := parserCmd.Start(); err != nil {
log.Printf("failed to initialize parser: %v\n", err)
os.Exit(1)
}
-
- go func() {
- if err := cmd.Wait(); err != nil {
- log.Printf("failed to wait for parser: %v\n", err)
- os.Exit(1)
- }
- }()
}
func shutdownParserProcess() {
if err := parserStdin.Close(); err != nil {
fmt.Fprintf(os.Stderr, "error closing parser: %v", err)
}
+
+ if err := parserCmd.Wait(); err != nil {
+ log.Printf("failed to wait for parser: %v\n", err)
+ }
}
// python3Parser implements a parser for Python files that extracts the modules
diff --git a/gazelle/python/std_modules.go b/gazelle/python/std_modules.go
index dd59cd8..8a016af 100644
--- a/gazelle/python/std_modules.go
+++ b/gazelle/python/std_modules.go
@@ -29,6 +29,7 @@
)
var (
+ stdModulesCmd *exec.Cmd
stdModulesStdin io.WriteCloser
stdModulesStdout io.Reader
stdModulesMutex sync.Mutex
@@ -40,42 +41,39 @@
// due to #691, we need a system interpreter to boostrap, part of which is
// to locate the hermetic interpreter.
- cmd := exec.CommandContext(ctx, "python3", helperPath, "std_modules")
- cmd.Stderr = os.Stderr
+ stdModulesCmd = exec.CommandContext(ctx, "python3", helperPath, "std_modules")
+ stdModulesCmd.Stderr = os.Stderr
// All userland site-packages should be ignored.
- cmd.Env = []string{"PYTHONNOUSERSITE=1"}
+ stdModulesCmd.Env = []string{"PYTHONNOUSERSITE=1"}
- stdin, err := cmd.StdinPipe()
+ stdin, err := stdModulesCmd.StdinPipe()
if err != nil {
log.Printf("failed to initialize std_modules: %v\n", err)
os.Exit(1)
}
stdModulesStdin = stdin
- stdout, err := cmd.StdoutPipe()
+ stdout, err := stdModulesCmd.StdoutPipe()
if err != nil {
log.Printf("failed to initialize std_modules: %v\n", err)
os.Exit(1)
}
stdModulesStdout = stdout
- if err := cmd.Start(); err != nil {
+ if err := stdModulesCmd.Start(); err != nil {
log.Printf("failed to initialize std_modules: %v\n", err)
os.Exit(1)
}
-
- go func() {
- if err := cmd.Wait(); err != nil {
- log.Printf("failed to wait for std_modules: %v\n", err)
- os.Exit(1)
- }
- }()
}
func shutdownStdModuleProcess() {
if err := stdModulesStdin.Close(); err != nil {
fmt.Fprintf(os.Stderr, "error closing std module: %v", err)
}
+
+ if err := stdModulesCmd.Wait(); err != nil {
+ log.Printf("failed to wait for std_modules: %v\n", err)
+ }
}
func isStdModule(m module) (bool, error) {