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" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 } | 43 } |
44 | 44 |
45 // Normalize normalizes the Interpreter configuration by resolving relative | 45 // Normalize normalizes the Interpreter configuration by resolving relative |
46 // paths into absolute paths and evaluating symlnks. | 46 // paths into absolute paths and evaluating symlnks. |
47 func (i *Interpreter) Normalize() error { | 47 func (i *Interpreter) Normalize() error { |
48 if err := filesystem.AbsPath(&i.Python); err != nil { | 48 if err := filesystem.AbsPath(&i.Python); err != nil { |
49 return err | 49 return err |
50 } | 50 } |
51 resolved, err := filepath.EvalSymlinks(i.Python) | 51 resolved, err := filepath.EvalSymlinks(i.Python) |
52 if err != nil { | 52 if err != nil { |
53 » » return errors.Annotate(err).Reason("could not evaluate symlinks
for: %(path)q"). | 53 » » return errors.Annotate(err, "could not evaluate symlinks for: %q
", i.Python).Err() |
54 » » » D("path", i.Python). | |
55 » » » Err() | |
56 } | 54 } |
57 i.Python = resolved | 55 i.Python = resolved |
58 return nil | 56 return nil |
59 } | 57 } |
60 | 58 |
61 // IsolatedCommand returns a configurable exec.Cmd structure bound to this | 59 // IsolatedCommand returns a configurable exec.Cmd structure bound to this |
62 // Interpreter. | 60 // Interpreter. |
63 // | 61 // |
64 // The supplied arguments have several Python isolation flags prepended to them | 62 // The supplied arguments have several Python isolation flags prepended to them |
65 // to remove environmental factors such as: | 63 // to remove environmental factors such as: |
(...skipping 25 matching lines...) Expand all Loading... |
91 if i.cachedVersion != nil { | 89 if i.cachedVersion != nil { |
92 v = *i.cachedVersion | 90 v = *i.cachedVersion |
93 return | 91 return |
94 } | 92 } |
95 | 93 |
96 // We use CombinedOutput here becuase Python2 writes the version to STDE
RR, | 94 // We use CombinedOutput here becuase Python2 writes the version to STDE
RR, |
97 // while Python3+ writes it to STDOUT. | 95 // while Python3+ writes it to STDOUT. |
98 cmd := i.IsolatedCommand(c, "--version") | 96 cmd := i.IsolatedCommand(c, "--version") |
99 out, err := cmd.CombinedOutput() | 97 out, err := cmd.CombinedOutput() |
100 if err != nil { | 98 if err != nil { |
101 » » err = errors.Annotate(err).Err() | 99 » » err = errors.Annotate(err, "").Err() |
102 return | 100 return |
103 } | 101 } |
104 | 102 |
105 if v, err = ParseVersionOutput(string(out)); err != nil { | 103 if v, err = ParseVersionOutput(string(out)); err != nil { |
106 return | 104 return |
107 } | 105 } |
108 | 106 |
109 i.cachedVersion = &v | 107 i.cachedVersion = &v |
110 return | 108 return |
111 } | 109 } |
112 | 110 |
113 // Hash returns the SHA256 hash string of this interpreter. | 111 // Hash returns the SHA256 hash string of this interpreter. |
114 // | 112 // |
115 // The hash value is cached; if called multiple times, the cached value will | 113 // The hash value is cached; if called multiple times, the cached value will |
116 // be returned. | 114 // be returned. |
117 func (i *Interpreter) Hash() (string, error) { | 115 func (i *Interpreter) Hash() (string, error) { |
118 hashInterpreter := func(path string) (string, error) { | 116 hashInterpreter := func(path string) (string, error) { |
119 fd, err := os.Open(i.Python) | 117 fd, err := os.Open(i.Python) |
120 if err != nil { | 118 if err != nil { |
121 » » » return "", errors.Annotate(err).Reason("failed to open i
nterpreter").Err() | 119 » » » return "", errors.Annotate(err, "failed to open interpre
ter").Err() |
122 } | 120 } |
123 defer fd.Close() | 121 defer fd.Close() |
124 | 122 |
125 hash := sha256.New() | 123 hash := sha256.New() |
126 if _, err := io.Copy(hash, fd); err != nil { | 124 if _, err := io.Copy(hash, fd); err != nil { |
127 » » » return "", errors.Annotate(err).Reason("failed to read [
%(path)s] for hashing"). | 125 » » » return "", errors.Annotate(err, "failed to read [%s] for
hashing", path).Err() |
128 » » » » D("path", path). | |
129 » » » » Err() | |
130 } | 126 } |
131 | 127 |
132 return hex.EncodeToString(hash.Sum(nil)), nil | 128 return hex.EncodeToString(hash.Sum(nil)), nil |
133 } | 129 } |
134 | 130 |
135 i.cachedHashOnce.Do(func() { | 131 i.cachedHashOnce.Do(func() { |
136 i.cachedHash, i.cachedHashErr = hashInterpreter(i.Python) | 132 i.cachedHash, i.cachedHashErr = hashInterpreter(i.Python) |
137 }) | 133 }) |
138 return i.cachedHash, i.cachedHashErr | 134 return i.cachedHash, i.cachedHashErr |
139 } | 135 } |
140 | 136 |
141 // ParseVersionOutput parses a Version out of the output of a "--version" Python | 137 // ParseVersionOutput parses a Version out of the output of a "--version" Python |
142 // invocation. | 138 // invocation. |
143 func ParseVersionOutput(output string) (Version, error) { | 139 func ParseVersionOutput(output string) (Version, error) { |
144 s := strings.TrimSpace(string(output)) | 140 s := strings.TrimSpace(string(output)) |
145 | 141 |
146 // Expected output: | 142 // Expected output: |
147 // Python X.Y.Z | 143 // Python X.Y.Z |
148 parts := strings.SplitN(s, " ", 2) | 144 parts := strings.SplitN(s, " ", 2) |
149 if len(parts) != 2 || parts[0] != "Python" { | 145 if len(parts) != 2 || parts[0] != "Python" { |
150 return Version{}, errors.Reason("unknown version output"). | 146 return Version{}, errors.Reason("unknown version output"). |
151 » » » D("output", s). | 147 » » » InternalReason("output(%q)", s).Err() |
152 » » » Err() | |
153 } | 148 } |
154 | 149 |
155 v, err := ParseVersion(parts[1]) | 150 v, err := ParseVersion(parts[1]) |
156 if err != nil { | 151 if err != nil { |
157 » » err = errors.Annotate(err).Reason("failed to parse version from:
%(value)q"). | 152 » » err = errors.Annotate(err, "failed to parse version from: %q", p
arts[1]).Err() |
158 » » » D("value", parts[1]). | |
159 » » » Err() | |
160 return v, err | 153 return v, err |
161 } | 154 } |
162 return v, nil | 155 return v, nil |
163 } | 156 } |
OLD | NEW |