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

Unified Diff: recipe_modules/context/api.py

Issue 2925453002: [context] Add path prefix/suffix manipulation. (Closed)
Patch Set: comments, use sentinels 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 side-by-side diff with in-line comments
Download patch
Index: recipe_modules/context/api.py
diff --git a/recipe_modules/context/api.py b/recipe_modules/context/api.py
index c1119ea275e2477c788f0706b32a2076d5092287..9cf24623aa397ebefbd7b43c074ebd30994f5ef7 100644
--- a/recipe_modules/context/api.py
+++ b/recipe_modules/context/api.py
@@ -26,6 +26,8 @@ Example:
import collections
+import os
+import types
from contextlib import contextmanager
@@ -40,7 +42,15 @@ def check_type(name, var, expect):
name, expect.__name__, var, type(var).__name__))
+_EnvPathComponent = collections.namedtuple('_EnvPathComponent', (
+ 'paths', 'is_prefix'))
+
+_EnvValue = collections.namedtuple('_EnvValue', (
+ 'str', 'prefixes', 'suffixes'))
iannucci 2017/06/07 23:17:59 prefixes, str, suffixes?
dnj 2017/06/07 23:48:46 Done.
+
+
class ContextApi(RecipeApi):
+
# TODO(iannucci): move implementation of these data directly into this class.
def __init__(self, **kwargs):
super(RecipeApi, self).__init__(**kwargs)
@@ -88,19 +98,20 @@ class ContextApi(RecipeApi):
Environmental Variable Overrides:
Env is a mapping of environment variable name to the value you want that
- environment variable to have. The value is a string, with a couple
- exceptions:
- * If value is None, this environment variable will be removed from the
- environment when the step runs.
- * String values will be %-formatted with the current value of the
- environment at the time the step runs. This means that you can have
- a value like:
- "/path/to/my/stuff:%(PATH)s"
+ environment variable to have. The value is either:
iannucci 2017/06/07 23:17:59 s/either/one of
dnj 2017/06/07 23:48:46 Done.
+ * None, indicating that the environment variable should be removed from
+ the environment when the step runs.
+ * A string value. Note that string values will be %-formatted with the
+ current value of the environment at the time the step runs. This means
+ that you can have a value like:
+ "/path/to/my/stuff:%(PATH)s"
Which, at the time the step executes, will inject the current value of
$PATH.
+ * A sentinel value such as Prefix or Suffix to attach a specific component
+ to a pathsep-delimited list variable.
- TODO(iannucci): implement env_paths which allows for easier manipulation of
- `pathsep` environment variables like $PATH, $PYTHONPATH, etc.
+ TODO(iannucci,dnj): Disallow "env" values to be mixes of string or
+ Prefix/Suffix.
TODO(iannucci): combine nest_level and name_prefix
@@ -154,23 +165,41 @@ class ContextApi(RecipeApi):
new = dict(self._env[-1])
for k, v in env.iteritems():
k = str(k)
+ ev = new.get(k)
+ if ev is None:
+ ev = _EnvValue(str=None, prefixes=(), suffixes=())
if v is not None:
- v = str(v)
- try:
- # This odd little piece of code does the following:
- # * add a bogus dictionary format %(foo)s to v. This forces % into
- # 'dictionary lookup' mode
- # * format the result with a defaultdict. This allows all
- # `%(key)s` format lookups to succeed, but any sequential `%s`
- # lookups to fail.
- # If the string contains any accidental sequential lookups, this
- # will raise an exception. If not, then this is a pluasible format
- # string.
- ('%(foo)s'+v) % collections.defaultdict(str)
- except Exception:
- raise ValueError(('Invalid %%-formatting parameter in envvar, '
- 'only %%(ENVVAR)s allowed: %r') % (v,))
- new[k] = v
+ if isinstance(v, _EnvPathComponent):
+ def _uniquify(*path_tuples):
+ seen = set()
+ for path_tuple in path_tuples:
+ for path in path_tuple:
+ if path not in seen:
+ seen.add(path)
+ yield path
+
+ if v.is_prefix:
+ ev = ev._replace(prefixes=tuple(_uniquify(v.paths, ev.prefixes)))
iannucci 2017/06/07 23:17:59 let's just make it append
dnj 2017/06/07 23:48:47 Done.
+ else:
+ ev = ev._replace(suffixes=tuple(_uniquify(ev.suffixes, v.paths)))
+ else:
+ v = str(v)
+ try:
+ # This odd little piece of code does the following:
+ # * add a bogus dictionary format %(foo)s to v. This forces %
+ # into 'dictionary lookup' mode
+ # * format the result with a defaultdict. This allows all
+ # `%(key)s` format lookups to succeed, but any sequential `%s`
+ # lookups to fail.
+ # If the string contains any accidental sequential lookups, this
+ # will raise an exception. If not, then this is a pluasible format
+ # string.
+ ('%(foo)s'+v) % collections.defaultdict(str)
+ except Exception:
+ raise ValueError(('Invalid %%-formatting parameter in envvar, '
+ 'only %%(ENVVAR)s allowed: %r') % (v,))
+ ev = ev._replace(str=v)
+ new[k] = ev
self._env.append(new)
to_pop.append(self._env)
@@ -203,7 +232,16 @@ class ContextApi(RecipeApi):
"""
# TODO(iannucci): store env in an immutable way to avoid excessive copies.
# TODO(iannucci): handle case-insensitive keys on windows
- return dict(self._env[-1])
+ def parts(ev):
+ for p in ev.prefixes:
+ yield str(p)
+ if ev.str is not None:
+ yield ev.str
+ for p in ev.suffixes:
+ yield str(p)
+
+ return {k: self.m.path.pathsep.join(parts(ev))
+ for k, ev in self._env[-1].iteritems()}
@property
def infra_step(self):
@@ -232,3 +270,33 @@ class ContextApi(RecipeApi):
Returns (int) - The current nesting level.
"""
return self._nest_level[-1]
+
+ def Prefix(self, *paths):
+ """Returns: an assignable "env" value that prefixes the specified paths to
+ the beginning of an environment variable.
+
+ Each path in paths is added, in order, as a prefix to the environment
+ variable, delimited by the OS path separator. This can be used for
+ easy manipulation of path environment variables such as PATH and PYTHONPATH.
+
+ Args:
+ paths (...Path): The list of paths to prefix.
+ """
+ for i, path in enumerate(paths):
+ check_type('path element %d' % (i,), path, Path)
+ return _EnvPathComponent(paths=paths, is_prefix=True)
+
+ def Suffix(self, *paths):
+ """Returns: an assignable "env" value that appends the specified paths to
+ the beginning of an environment variable.
+
+ Each path in paths is added, in order, as a suffix to the environment
+ variable, delimited by the OS path separator. This can be used for
+ easy manipulation of path environment variables such as PATH and PYTHONPATH.
+
+ Args:
+ paths (...Path): The list of paths to suffix.
+ """
+ for i, path in enumerate(paths):
+ check_type('path element %d' % (i,), path, Path)
+ return _EnvPathComponent(paths=paths, is_prefix=False)

Powered by Google App Engine
This is Rietveld 408576698