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

Side by Side Diff: vpython/run.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/python/python_test.go ('k') | vpython/system_posix.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package vpython
6
7 import (
8 "os"
9 "os/exec"
10 "os/signal"
11
12 "github.com/luci/luci-go/common/errors"
13 "github.com/luci/luci-go/common/logging"
14 "github.com/luci/luci-go/common/system/environ"
15 "github.com/luci/luci-go/vpython/venv"
16
17 "golang.org/x/net/context"
18 )
19
20 var (
21 // EnvSpecPath is the exported enviornment variable for the specificatio n path.
22 //
23 // This is added to the bootstrap enviornment used by Run to allow subpr ocess
24 // "vpython" invocations to automatically inherit the same environment.
25 EnvSpecPath = "VPYTHON_VENV_SPEC_PATH"
26 )
27
28 // Run sets up a Python VirtualEnv and executes the supplied Options.
29 //
30 // Run returns nil if if the Python environment was successfully set-up and the
31 // Python interpreter was successfully run with a zero return code. If the
32 // Python interpreter returns a non-zero return code, a PythonError (potentially
33 // wrapped) will be returned.
34 //
35 // A generalized return code to return for an error value can be obtained via
36 // ReturnCode.
37 //
38 // Run consists of:
39 //
40 // - Identify the target Python script to run (if there is one).
41 // - Identifying the Python interpreter to use.
42 // - Composing the environment specification.
43 // - Constructing the virtual environment (download, install).
44 // - Execute the Python process with the supplied arguments.
45 //
46 // The Python subprocess is bound to the lifetime of ctx, and will be terminated
47 // if ctx is cancelled.
48 func Run(c context.Context, opts Options) error {
49 // Resolve our Options.
50 if err := opts.resolve(c); err != nil {
51 return errors.Annotate(err).Reason("could not resolve options"). Err()
52 }
53
54 // Create a local cancellation option (signal handling).
55 c, cancelFunc := context.WithCancel(c)
56 defer cancelFunc()
57
58 // Create our virtual enviornment root directory.
59 err := venv.With(c, opts.EnvConfig, opts.WaitForEnv, func(c context.Cont ext, ve *venv.Env) error {
60 // Build the augmented environment variables.
61 e := opts.Environ
62 if e.Len() == 0 {
63 // If no environment was supplied, use the system enviro nment.
64 e = environ.System()
65 }
66
67 e.Set("VIRTUAL_ENV", ve.Root) // Set by VirtualEnv script.
68 if ve.SpecPath != "" {
69 e.Set(EnvSpecPath, ve.SpecPath)
70 }
71
72 // Run our bootstrapped Python command.
73 cmd := ve.InterpreterCommand()
74 cmd.WorkDir = opts.WorkDir
75 cmd.Isolated = true
76 cmd.Env = e.Sorted()
77
78 pythonCmd, err := cmd.Prepare(c, opts.Args...)
79 if err != nil {
80 return errors.Annotate(err).Reason("failed to prepare co mmand").Err()
81 }
82 pythonCmd.Stdin = os.Stdin
83
84 if err := runAndForwardSignals(c, pythonCmd, cancelFunc); err != nil {
85 return errors.Annotate(err).Reason("failed to execute bo otstrapped Python").Err()
86 }
87 return nil
88 })
89 if err != nil {
90 return errors.Annotate(err).Err()
91 }
92 return nil
93 }
94
95 func runAndForwardSignals(c context.Context, cmd *exec.Cmd, cancelFunc context.C ancelFunc) error {
96 signalC := make(chan os.Signal, 1)
97 signalDoneC := make(chan struct{})
98 signal.Notify(signalC, forwardedSignals...)
99 defer func() {
100 signal.Stop(signalC)
101
102 close(signalC)
103 <-signalDoneC
104 }()
105
106 if err := cmd.Start(); err != nil {
107 return errors.Annotate(err).Reason("failed to start process").Er r()
108 }
109
110 logging.Fields{
111 "pid": cmd.Process.Pid,
112 }.Debugf(c, "Python subprocess has started!")
113
114 // Start our signal forwarding goroutine, now that the process is runnin g.
115 go func() {
116 defer func() {
117 close(signalDoneC)
118 }()
119
120 for sig := range signalC {
121 logging.Debugf(c, "Forwarding signal: %v", sig)
122 if err := cmd.Process.Signal(sig); err != nil {
123 logging.Fields{
124 logging.ErrorKey: err,
125 "signal": sig,
126 }.Errorf(c, "Failed to forward signal; terminati ng immediately.")
127 cancelFunc()
128 }
129 }
130 }()
131
132 err := cmd.Wait()
133 logging.Debugf(c, "Python subprocess has terminated: %v", err)
134 if err != nil {
135 return errors.Annotate(err).Err()
136 }
137 return nil
138 }
OLDNEW
« no previous file with comments | « vpython/python/python_test.go ('k') | vpython/system_posix.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698