| 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 """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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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) |
| OLD | NEW |