Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1029)

Side by Side Diff: vpython/python/interpreter.go

Issue 2702873002: vpython: Add primary execution package. (Closed)
Patch Set: more windows signals Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « vpython/options.go ('k') | vpython/python/python.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "os" 8 "os"
9 "os/exec" 9 "os/exec"
10 "strings" 10 "strings"
11 "sync" 11 "sync"
12 12
13 "github.com/luci/luci-go/common/errors" 13 "github.com/luci/luci-go/common/errors"
14 "github.com/luci/luci-go/common/logging" 14 "github.com/luci/luci-go/common/logging"
15 "github.com/luci/luci-go/common/system/exitcode"
16 15
17 "golang.org/x/net/context" 16 "golang.org/x/net/context"
18 ) 17 )
19 18
20 type runnerFunc func(cmd *exec.Cmd, capture bool) (string, error) 19 type runnerFunc func(cmd *exec.Cmd, capture bool) (string, error)
21 20
22 // Interpreter represents a system Python interpreter. It exposes the ability 21 // Interpreter represents a system Python interpreter. It exposes the ability
23 // to use common functionality of that interpreter. 22 // to use common functionality of that interpreter.
24 type Interpreter struct { 23 type Interpreter struct {
25 // Python is the path to the system Python interpreter. 24 // Python is the path to the system Python interpreter.
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 type Command struct { 108 type Command struct {
110 // Python is the path to the Python interpreter to use. It is automatica lly 109 // Python is the path to the Python interpreter to use. It is automatica lly
111 // populated from an Interpreter when created through an Interpreter's C ommand 110 // populated from an Interpreter when created through an Interpreter's C ommand
112 // method. 111 // method.
113 Python string 112 Python string
114 113
115 // WorkDir is the working directory to use when running the interpreter. If 114 // WorkDir is the working directory to use when running the interpreter. If
116 // empty, the current working directory will be used. 115 // empty, the current working directory will be used.
117 WorkDir string 116 WorkDir string
118 117
119 // ConnectSTDIN will cause this process' STDIN to be passed through to t he
120 // Python subprocess. Otherwise, the Python subprocess will receive a cl osed
121 // STDIN.
122 ConnectSTDIN bool
123
124 // Isolated means that the Python invocation should include flags to iso late 118 // Isolated means that the Python invocation should include flags to iso late
125 // it from local system modification. 119 // it from local system modification.
126 // 120 //
127 // This removes environmental factors such as: 121 // This removes environmental factors such as:
128 // - The user's "site.py". 122 // - The user's "site.py".
129 // - The current PYTHONPATH environment variable. 123 // - The current PYTHONPATH environment variable.
130 // - Compiled ".pyc/.pyo" files. 124 // - Compiled ".pyc/.pyo" files.
131 Isolated bool 125 Isolated bool
132 126
133 // Env, if not nil, is the environment to supply. 127 // Env, if not nil, is the environment to supply.
134 Env []string 128 Env []string
135 129
136 // testRunner is the runner function to use to run an exec.Cmd. This can be 130 // testRunner is the runner function to use to run an exec.Cmd. This can be
137 // swapped out for testing. 131 // swapped out for testing.
138 testRunner runnerFunc 132 testRunner runnerFunc
139 } 133 }
140 134
141 // Run runs the configured Command with the supplied arguments. 135 // Prepare generates an exec.Cmd with the Command's configuration.
142 // 136 func (ic *Command) Prepare(c context.Context, args ...string) (*exec.Cmd, error) {
143 // Run returns wrapped errors. Use errors.Unwrap to get the main cause, if
144 // needed. If an error occurs during setup or invocation, it will be returned
145 // directly. If the interpreter runs and returns zero, nil will be returned. If
146 // the interpreter runs and returns non-zero, an Error instance will be returned
147 // containing that return code.
148 func (ic *Command) Run(c context.Context, args ...string) error {
149 if ic.Python == "" { 137 if ic.Python == "" {
150 » » return errors.New("a Python interpreter must be supplied") 138 » » return nil, errors.New("a Python interpreter must be supplied")
151 } 139 }
152 140
153 if ic.Isolated { 141 if ic.Isolated {
154 args = append([]string{ 142 args = append([]string{
155 "-B", // Don't compile "pyo" binaries. 143 "-B", // Don't compile "pyo" binaries.
156 "-E", // Don't use PYTHON* enviornment variables. 144 "-E", // Don't use PYTHON* enviornment variables.
157 "-s", // Don't use user 'site.py'. 145 "-s", // Don't use user 'site.py'.
158 }, args...) 146 }, args...)
159 } 147 }
160 148
161 cmd := exec.CommandContext(c, ic.Python, args...) 149 cmd := exec.CommandContext(c, ic.Python, args...)
162 cmd.Stdout = os.Stdout 150 cmd.Stdout = os.Stdout
163 cmd.Stderr = os.Stderr 151 cmd.Stderr = os.Stderr
164 if ic.ConnectSTDIN {
165 cmd.Stdin = os.Stdin
166 }
167 cmd.Dir = ic.WorkDir 152 cmd.Dir = ic.WorkDir
168 cmd.Env = ic.Env 153 cmd.Env = ic.Env
169 154
155 return cmd, nil
156 }
157
158 // Run runs the configured Command with the supplied arguments.
159 //
160 // Run returns wrapped errors. Use errors.Unwrap to get the main cause, if
161 // needed. If an error occurs during setup or invocation, including an exit
162 // code related error, it will be returned, possibly wrapped. If the interpreter
163 // runs and returns zero, nil will be returned.
164 func (ic *Command) Run(c context.Context, args ...string) error {
165 cmd, err := ic.Prepare(c, args...)
166 if err != nil {
167 return errors.Annotate(err).Err()
168 }
169
170 if logging.IsLogging(c, logging.Debug) { 170 if logging.IsLogging(c, logging.Debug) {
171 logging.Debugf(c, "Running Python command (cwd=%s): %s", 171 logging.Debugf(c, "Running Python command (cwd=%s): %s",
172 cmd.Dir, strings.Join(cmd.Args, " ")) 172 cmd.Dir, strings.Join(cmd.Args, " "))
173 } 173 }
174 174
175 // Allow testing to supply an alternative runner function. 175 // Allow testing to supply an alternative runner function.
176 rf := ic.testRunner 176 rf := ic.testRunner
177 if rf == nil { 177 if rf == nil {
178 rf = defaultRunnerFunc 178 rf = defaultRunnerFunc
179 } 179 }
180 180
181 if _, err := rf(cmd, false); err != nil { 181 if _, err := rf(cmd, false); err != nil {
182 // If the process failed because of a non-zero return value, ret urn that
183 // as our error.
184 if rc, has := exitcode.Get(err); has {
185 return errors.Annotate(Error(rc)).Reason("Python bootstr ap returned non-zero").Err()
186 }
187
188 return errors.Annotate(err).Reason("failed to run Python command "). 182 return errors.Annotate(err).Reason("failed to run Python command ").
189 D("python", ic.Python). 183 D("python", ic.Python).
190 D("args", args). 184 D("args", args).
191 Err() 185 Err()
192 } 186 }
193 return nil 187 return nil
194 } 188 }
195 189
196 func defaultRunnerFunc(cmd *exec.Cmd, capture bool) (string, error) { 190 func defaultRunnerFunc(cmd *exec.Cmd, capture bool) (string, error) {
197 // If we're capturing output, combine STDOUT and STDERR (see GetVersion 191 // If we're capturing output, combine STDOUT and STDERR (see GetVersion
198 // for details). 192 // for details).
199 if capture { 193 if capture {
200 out, err := cmd.CombinedOutput() 194 out, err := cmd.CombinedOutput()
201 if err != nil { 195 if err != nil {
202 return "", errors.Annotate(err).Err() 196 return "", errors.Annotate(err).Err()
203 } 197 }
204 return string(out), nil 198 return string(out), nil
205 } 199 }
206 200
207 // Non-capturing run. 201 // Non-capturing run.
208 if err := cmd.Run(); err != nil { 202 if err := cmd.Run(); err != nil {
209 return "", errors.Annotate(err).Err() 203 return "", errors.Annotate(err).Err()
210 } 204 }
211 return "", nil 205 return "", nil
212 } 206 }
OLDNEW
« no previous file with comments | « vpython/options.go ('k') | vpython/python/python.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698