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

Side by Side Diff: recipe_modules/context/api.py

Issue 2920223011: [context] fix treatment of None in env. (Closed)
Patch Set: really fix it Created 3 years, 6 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 | « no previous file | recipe_modules/context/tests/env.py » ('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 """The context module provides APIs for manipulating a few pieces of 'ambient' 5 """The context module provides APIs for manipulating a few pieces of 'ambient'
6 data that affect how steps are run: 6 data that affect how steps are run:
7 cwd - The current working directory. 7 cwd - The current working directory.
8 env - The environment variables. 8 env - The environment variables.
9 infra_step - Whether or not failures should be treated as infrastructure 9 infra_step - Whether or not failures should be treated as infrastructure
10 failures vs. normal failures. 10 failures vs. normal failures.
(...skipping 27 matching lines...) Expand all
38 38
39 def check_type(name, var, expect): 39 def check_type(name, var, expect):
40 if not isinstance(var, expect): # pragma: no cover 40 if not isinstance(var, expect): # pragma: no cover
41 raise TypeError('%s is not %s: %r (%s)' % ( 41 raise TypeError('%s is not %s: %r (%s)' % (
42 name, expect.__name__, var, type(var).__name__)) 42 name, expect.__name__, var, type(var).__name__))
43 43
44 44
45 _EnvPathComponent = collections.namedtuple('_EnvPathComponent', ( 45 _EnvPathComponent = collections.namedtuple('_EnvPathComponent', (
46 'paths',)) 46 'paths',))
47 47
48 # prefixes is a list of strings
49 # value is either a string or None
48 _EnvValue = collections.namedtuple('_EnvValue', ( 50 _EnvValue = collections.namedtuple('_EnvValue', (
49 'prefixes', 'str')) 51 'prefixes', 'value'))
50 52
51 53
52 class ContextApi(RecipeApi): 54 class ContextApi(RecipeApi):
53 55
54 # TODO(iannucci): move implementation of these data directly into this class. 56 # TODO(iannucci): move implementation of these data directly into this class.
55 def __init__(self, **kwargs): 57 def __init__(self, **kwargs):
56 super(RecipeApi, self).__init__(**kwargs) 58 super(RecipeApi, self).__init__(**kwargs)
57 59
58 self._cwd = [None] 60 self._cwd = [None]
59 self._env = [{}] 61 self._env = [{}]
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 else: 146 else:
145 self._name_prefix.append(name_prefix) 147 self._name_prefix.append(name_prefix)
146 to_pop.append(self._name_prefix) 148 to_pop.append(self._name_prefix)
147 149
148 if env is not None and env != {}: 150 if env is not None and env != {}:
149 check_type('env', env, dict) 151 check_type('env', env, dict)
150 # we hit _env directly to avoid an extra copy. 152 # we hit _env directly to avoid an extra copy.
151 new = dict(self._env[-1]) 153 new = dict(self._env[-1])
152 for k, v in env.iteritems(): 154 for k, v in env.iteritems():
153 k = str(k) 155 k = str(k)
154 ev = new.get(k) 156 if v is None:
155 if ev is None: 157 ev = _EnvValue(prefixes=(), value=None)
156 ev = _EnvValue(prefixes=(), str=None) 158 else:
157 if v is not None: 159 ev = new.get(k, _EnvValue(prefixes=(), value=''))
158 if isinstance(v, _EnvPathComponent): 160 if isinstance(v, _EnvPathComponent):
159 ev = ev._replace(prefixes=v.paths+ev.prefixes) 161 ev = ev._replace(prefixes=v.paths+ev.prefixes)
160 else: 162 else:
161 v = str(v) 163 v = str(v)
162 try: 164 try:
163 # This odd little piece of code does the following: 165 # This odd little piece of code does the following:
164 # * add a bogus dictionary format %(foo)s to v. This forces % 166 # * add a bogus dictionary format %(foo)s to v. This forces %
165 # into 'dictionary lookup' mode 167 # into 'dictionary lookup' mode
166 # * format the result with a defaultdict. This allows all 168 # * format the result with a defaultdict. This allows all
167 # `%(key)s` format lookups to succeed, but any sequential `%s` 169 # `%(key)s` format lookups to succeed, but any sequential `%s`
168 # lookups to fail. 170 # lookups to fail.
169 # If the string contains any accidental sequential lookups, this 171 # If the string contains any accidental sequential lookups, this
170 # will raise an exception. If not, then this is a pluasible format 172 # will raise an exception. If not, then this is a pluasible format
171 # string. 173 # string.
172 ('%(foo)s'+v) % collections.defaultdict(str) 174 ('%(foo)s'+v) % collections.defaultdict(str)
173 except Exception: 175 except Exception:
174 raise ValueError(('Invalid %%-formatting parameter in envvar, ' 176 raise ValueError(('Invalid %%-formatting parameter in envvar, '
175 'only %%(ENVVAR)s allowed: %r') % (v,)) 177 'only %%(ENVVAR)s allowed: %r') % (v,))
176 ev = ev._replace(str=v) 178 ev = ev._replace(value=v)
177 new[k] = ev 179 new[k] = ev
178 self._env.append(new) 180 self._env.append(new)
179 to_pop.append(self._env) 181 to_pop.append(self._env)
180 182
181 try: 183 try:
182 yield 184 yield
183 finally: 185 finally:
184 for p in to_pop: 186 for p in to_pop:
185 p.pop() 187 p.pop()
186 188
(...skipping 16 matching lines...) Expand all
203 done with properties. 205 done with properties.
204 206
205 Returns (dict) - The env-key -> value mapping of current environment 207 Returns (dict) - The env-key -> value mapping of current environment
206 modifications. 208 modifications.
207 """ 209 """
208 # TODO(iannucci): store env in an immutable way to avoid excessive copies. 210 # TODO(iannucci): store env in an immutable way to avoid excessive copies.
209 # TODO(iannucci): handle case-insensitive keys on windows 211 # TODO(iannucci): handle case-insensitive keys on windows
210 def parts(ev): 212 def parts(ev):
211 for p in ev.prefixes: 213 for p in ev.prefixes:
212 yield str(p) 214 yield str(p)
213 if ev.str is not None: 215 if ev.value:
214 yield ev.str 216 yield ev.value
215 217
216 return {k: self.m.path.pathsep.join(parts(ev)) 218 ret = {}
217 for k, ev in self._env[-1].iteritems()} 219 for k, ev in self._env[-1].iteritems():
220 if ev.value is None:
221 ret[k] = None
222 else:
223 ret[k] = self.m.path.pathsep.join(parts(ev))
224
225 return ret
218 226
219 @property 227 @property
220 def infra_step(self): 228 def infra_step(self):
221 """Returns the current value of the infra_step setting. 229 """Returns the current value of the infra_step setting.
222 230
223 Returns (bool) - True iff steps are currently considered infra steps. 231 Returns (bool) - True iff steps are currently considered infra steps.
224 """ 232 """
225 return self._infra_step[-1] 233 return self._infra_step[-1]
226 234
227 @property 235 @property
(...skipping 23 matching lines...) Expand all
251 Each path in paths is added, in order, as a prefix to the environment 259 Each path in paths is added, in order, as a prefix to the environment
252 variable, delimited by the OS path separator. This can be used for 260 variable, delimited by the OS path separator. This can be used for
253 easy manipulation of path environment variables such as PATH and PYTHONPATH. 261 easy manipulation of path environment variables such as PATH and PYTHONPATH.
254 262
255 Args: 263 Args:
256 paths (...Path): The list of paths to prefix. 264 paths (...Path): The list of paths to prefix.
257 """ 265 """
258 for i, path in enumerate(paths): 266 for i, path in enumerate(paths):
259 check_type('path element %d' % (i,), path, Path) 267 check_type('path element %d' % (i,), path, Path)
260 return _EnvPathComponent(paths=paths) 268 return _EnvPathComponent(paths=paths)
OLDNEW
« no previous file with comments | « no previous file | recipe_modules/context/tests/env.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698