| OLD | NEW |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package python | 5 package python |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "crypto/sha256" | 8 "crypto/sha256" |
| 9 "encoding/hex" | 9 "encoding/hex" |
| 10 "io" | 10 "io" |
| 11 "os" | 11 "os" |
| 12 "os/exec" | 12 "os/exec" |
| 13 "path/filepath" | 13 "path/filepath" |
| 14 "strings" | 14 "strings" |
| 15 "sync" | 15 "sync" |
| 16 | 16 |
| 17 "github.com/luci/luci-go/common/errors" | 17 "github.com/luci/luci-go/common/errors" |
| 18 "github.com/luci/luci-go/common/system/filesystem" | 18 "github.com/luci/luci-go/common/system/filesystem" |
| 19 | 19 |
| 20 "golang.org/x/net/context" | 20 "golang.org/x/net/context" |
| 21 ) | 21 ) |
| 22 | 22 |
| 23 type runnerFunc func(cmd *exec.Cmd, capture bool) (string, error) | |
| 24 | |
| 25 // Interpreter represents a system Python interpreter. It exposes the ability | 23 // Interpreter represents a system Python interpreter. It exposes the ability |
| 26 // to use common functionality of that interpreter. | 24 // to use common functionality of that interpreter. |
| 27 type Interpreter struct { | 25 type Interpreter struct { |
| 28 // Python is the path to the system Python interpreter. | 26 // Python is the path to the system Python interpreter. |
| 29 Python string | 27 Python string |
| 30 | 28 |
| 31 // cachedVersion is the cached Version for this interpreter. It is popul
ated | 29 // cachedVersion is the cached Version for this interpreter. It is popul
ated |
| 32 // on the first GetVersion call. | 30 // on the first GetVersion call. |
| 33 cachedVersion *Version | 31 cachedVersion *Version |
| 34 cachedVersionMu sync.Mutex | 32 cachedVersionMu sync.Mutex |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 | 95 |
| 98 // We use CombinedOutput here becuase Python2 writes the version to STDE
RR, | 96 // We use CombinedOutput here becuase Python2 writes the version to STDE
RR, |
| 99 // while Python3+ writes it to STDOUT. | 97 // while Python3+ writes it to STDOUT. |
| 100 cmd := i.IsolatedCommand(c, "--version") | 98 cmd := i.IsolatedCommand(c, "--version") |
| 101 out, err := cmd.CombinedOutput() | 99 out, err := cmd.CombinedOutput() |
| 102 if err != nil { | 100 if err != nil { |
| 103 err = errors.Annotate(err).Err() | 101 err = errors.Annotate(err).Err() |
| 104 return | 102 return |
| 105 } | 103 } |
| 106 | 104 |
| 107 » if v, err = parseVersionOutput(strings.TrimSpace(string(out))); err != n
il { | 105 » if v, err = ParseVersionOutput(out); err != nil { |
| 108 » » err = errors.Annotate(err).Err() | |
| 109 return | 106 return |
| 110 } | 107 } |
| 111 | 108 |
| 112 i.cachedVersion = &v | 109 i.cachedVersion = &v |
| 113 return | 110 return |
| 114 } | 111 } |
| 115 | 112 |
| 116 // Hash returns the SHA256 hash string of this interpreter. | 113 // Hash returns the SHA256 hash string of this interpreter. |
| 117 // | 114 // |
| 118 // The hash value is cached; if called multiple times, the cached value will | 115 // The hash value is cached; if called multiple times, the cached value will |
| (...skipping 15 matching lines...) Expand all Loading... |
| 134 | 131 |
| 135 return hex.EncodeToString(hash.Sum(nil)), nil | 132 return hex.EncodeToString(hash.Sum(nil)), nil |
| 136 } | 133 } |
| 137 | 134 |
| 138 i.cachedHashOnce.Do(func() { | 135 i.cachedHashOnce.Do(func() { |
| 139 i.cachedHash, i.cachedHashErr = hashInterpreter(i.Python) | 136 i.cachedHash, i.cachedHashErr = hashInterpreter(i.Python) |
| 140 }) | 137 }) |
| 141 return i.cachedHash, i.cachedHashErr | 138 return i.cachedHash, i.cachedHashErr |
| 142 } | 139 } |
| 143 | 140 |
| 144 func parseVersionOutput(output string) (Version, error) { | 141 // ParseVersionOutput parses a Version out of the output of a "--version" Python |
| 142 // invocation. |
| 143 func ParseVersionOutput(output []byte) (Version, error) { |
| 144 » s := strings.TrimSpace(string(output)) |
| 145 |
| 145 // Expected output: | 146 // Expected output: |
| 146 // Python X.Y.Z | 147 // Python X.Y.Z |
| 147 » parts := strings.SplitN(output, " ", 2) | 148 » parts := strings.SplitN(s, " ", 2) |
| 148 if len(parts) != 2 || parts[0] != "Python" { | 149 if len(parts) != 2 || parts[0] != "Python" { |
| 149 return Version{}, errors.Reason("unknown version output"). | 150 return Version{}, errors.Reason("unknown version output"). |
| 150 » » » D("output", output). | 151 » » » D("output", s). |
| 151 Err() | 152 Err() |
| 152 } | 153 } |
| 153 | 154 |
| 154 v, err := ParseVersion(parts[1]) | 155 v, err := ParseVersion(parts[1]) |
| 155 if err != nil { | 156 if err != nil { |
| 156 err = errors.Annotate(err).Reason("failed to parse version from:
%(value)q"). | 157 err = errors.Annotate(err).Reason("failed to parse version from:
%(value)q"). |
| 157 D("value", parts[1]). | 158 D("value", parts[1]). |
| 158 Err() | 159 Err() |
| 159 return v, err | 160 return v, err |
| 160 } | 161 } |
| 161 return v, nil | 162 return v, nil |
| 162 } | 163 } |
| OLD | NEW |