Chromium Code Reviews| Index: vpython/application/probe.go |
| diff --git a/vpython/application/probe.go b/vpython/application/probe.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fed252dac705206ca3112aacfee298300aeb5091 |
| --- /dev/null |
| +++ b/vpython/application/probe.go |
| @@ -0,0 +1,102 @@ |
| +// Copyright 2017 The LUCI Authors. All rights reserved. |
| +// Use of this source code is governed under the Apache License, Version 2.0 |
| +// that can be found in the LICENSE file. |
| + |
| +package application |
| + |
| +import ( |
| + "os/exec" |
| + |
| + "golang.org/x/net/context" |
| + |
| + "github.com/luci/luci-go/vpython/python" |
| + |
| + "github.com/luci/luci-go/common/errors" |
| + "github.com/luci/luci-go/common/logging" |
| + "github.com/luci/luci-go/common/system/environ" |
| + "github.com/luci/luci-go/common/system/exitcode" |
| + "github.com/luci/luci-go/common/system/prober" |
| +) |
| + |
| +const ( |
| + // CheckWrapperENV is an environment variable that is used to detect if a |
| + // given binary is a "vpython" wrapper instance. The "vpython" Main function |
| + // will immediately exit with a non-zero return code if this environment |
| + // variable is set. |
| + // |
| + // See checkWrapper for more details. |
| + CheckWrapperENV = "VPYTHON_CHECK_WRAPPER" |
| +) |
| + |
| +type lookPath struct { |
| + // probeBase is a base prober whose Self and SelfStat fields will be used |
| + // during a lookPathImpl call. This ensures that we only have to call |
| + // ResolveSelf once for any given application. |
| + probeBase prober.Probe |
| + |
| + env environ.Env |
| + lastPath string |
| + lastVersionOutput []byte |
| +} |
| + |
| +func (lp *lookPath) look(c context.Context, target string) (*python.LookPathResult, error) { |
| + // Construct our probe, derived it from our "probeBase" and tailored to the |
| + // specific "target" we're looking for. |
| + probe := lp.probeBase |
| + probe.Target = target |
| + probe.CheckWrapper = lp.checkWrapper |
| + |
| + var result python.LookPathResult |
| + var err error |
| + if result.Path, err = probe.Locate(c, "", lp.env.Clone()); err != nil { |
| + return nil, err |
| + } |
| + |
| + // During probing, we try and capture the output of "--version". If we do, |
| + // and if we confirm that the "path" returned by the probe matches "lastPath", |
| + // return this version along with the resolution. |
| + if result.Path == lp.lastPath { |
| + var err error |
| + if result.Version, err = python.ParseVersionOutput(lp.lastVersionOutput); err == nil { |
| + logging.Debugf(c, "Detected Python version %q from probe candidate [%s]", result.Version, result.Path) |
| + } else { |
| + logging.Debugf(c, "Failed to parse version from probe candidate [%s]: %s", result.Path, err) |
| + } |
| + } |
| + |
| + return &result, nil |
| +} |
| + |
| +// checkWrapper is a prober.CheckWrapperFunc that detects if a given path is a |
| +// "vpython" instance. |
| +// |
| +// It does this by running it "path --version" with CheckWrapperENV set. If |
| +// the target is a "vpython" wrapper, it will immediately exit with a non-zero |
| +// value (see Main). If it is not a valid Python program, its behavior is |
| +// undefined. If it is a valid Pytohn instance, it will exit with a Python |
| +// version string. |
| +// |
| +// As a side effect, the text output of the last version probe will be stored |
| +// in lp.lastVersionOutput. The "look" function can pass this value on to |
| +// the LookPathResult. |
| +func (lp *lookPath) checkWrapper(c context.Context, path string, env environ.Env) (isWrapper bool, err error) { |
| + lp.lastPath = path |
| + env.Set(CheckWrapperENV, "1") |
| + |
| + cmd := exec.CommandContext(c, path, "--version") |
| + cmd.Env = env.Sorted() |
| + |
| + lp.lastVersionOutput, err = cmd.CombinedOutput() |
| + rc, ok := exitcode.Get(err) |
| + if !ok { |
| + err = errors.Annotate(err).Reason("failed to check if %(path)q is a wrapper"). |
| + D("path", path). |
| + Err() |
| + return |
| + } |
| + |
| + // If "--version" didn't return "0", we determine that this is a wrapper, |
| + // since no Python interpreter would return non-zero for "--version". |
|
iannucci
2017/06/07 17:40:26
it might if it failed to load properly though, rig
dnj
2017/06/07 17:43:26
If it fails to load properly, we should not use it
dnj
2017/06/07 17:45:51
OTOH if Python is broken, I don't want "vpython" t
|
| + isWrapper, err = (rc != 0), nil |
| + return |
| +} |