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

Unified Diff: recipe_modules/context/api.py

Issue 2925453002: [context] Add path prefix/suffix manipulation. (Closed)
Patch Set: comments 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..b102ef05b3e63ab0cf3d9847207c958013e9ca80 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',))
+
+_EnvValue = collections.namedtuple('_EnvValue', (
+ 'prefixes', 'str'))
+
+
class ContextApi(RecipeApi):
+
# TODO(iannucci): move implementation of these data directly into this class.
def __init__(self, **kwargs):
super(RecipeApi, self).__init__(**kwargs)
@@ -88,37 +98,24 @@ 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 one of:
+ * 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 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
- Example:
- # suppose the OS's envar $OTHER is set to "yes"
- with api.context(env={'ENV_VAR': 'something:%(OTHER)s'}):
- # environment updates are additive.
- with api.context(env={'OTHER': 'cool:%(OTHER)s'}):
- # echos 'something:yes'
- # Note that the substitution always happens with the system
- # environment, not any of the computed environment here.
- api.step("check $ENV_VAR", ['bash', '-c', 'echo $ENV_VAR'])
- # echos 'cool:yes'
- api.step("check $OTHER", ['bash', '-c', 'echo $OTHER'])
-
- with api.context(env={'OTHER': None}):
- # echos ''
- api.step("check $OTHER", ['bash', '-c', 'echo $OTHER'])
+ Look at the examples in "examples/" for examples of context module usage.
"""
to_pop = []
@@ -154,23 +151,30 @@ 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(prefixes=(), str=None)
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):
+ ev = ev._replace(prefixes=v.paths+ev.prefixes)
+ 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 +207,14 @@ 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
+
+ return {k: self.m.path.pathsep.join(parts(ev))
+ for k, ev in self._env[-1].iteritems()}
@property
def infra_step(self):
@@ -232,3 +243,18 @@ 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)

Powered by Google App Engine
This is Rietveld 408576698