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

Unified Diff: recipe_engine/recipe_api.py

Issue 2934583003: [step] Remove config indirection. (Closed)
Patch Set: fix test 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
« no previous file with comments | « no previous file | recipe_engine/run.py » ('j') | recipe_modules/step/api.py » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: recipe_engine/recipe_api.py
diff --git a/recipe_engine/recipe_api.py b/recipe_engine/recipe_api.py
index 6ec91c3137bcfc74aa533b73cbc9083ce8df6773..f9eba82c3cae9ddcf639a5200c563cc550fb15e9 100644
--- a/recipe_engine/recipe_api.py
+++ b/recipe_engine/recipe_api.py
@@ -16,8 +16,7 @@ from functools import wraps
from .recipe_test_api import DisabledTestData, ModuleTestData
from .config import Single
-
-from .util import ModuleInjectionSite
+from .util import ModuleInjectionSite, Placeholder
class UnknownRequirementError(object):
@@ -172,6 +171,132 @@ class StepClient(object):
IDENT = 'step'
+
+ class StepConfig(collections.namedtuple('_StepConfig', (
+ 'name', 'base_name', 'cmd', 'cwd', 'env', 'allow_subannotations',
+ 'trigger_specs', 'timeout', 'infra_step', 'stdout', 'stderr', 'stdin',
+ 'ok_ret', 'step_test_data', 'nest_level'))):
+
+ """
+ StepConfig is the representation of a raw step as the recipe_engine sees it.
+ You should use the standard 'step' recipe module, which will construct and
+ pass this data to the engine for you, instead. The only reason why you would
+ need to worry about this object is if you're modifying the step module
+ itself.
+
+ Fields:
+ name (str): name of the step, will appear in buildbots waterfall
+ base_name (str): the base name of the step. If the step has a derived
+ name (e.g., nested may be concatenated with its parent), this is the
+ name component of just this step. If None, this will be set to "name".
+ cmd: command to run. Acceptable types: str, Path, Placeholder, or None.
+ cwd (str or None): absolute path to working directory for the command
+ env (dict): overrides for environment variables, described above.
+ allow_subannotations (bool): if True, lets the step emit its own
+ annotations. NOTE: Enabling this can cause some buggy behavior. Please
+ strongly consider using step_result.presentation instead. If you have
+ questions, please contact infra-dev@chromium.org.
+ trigger_specs: a list of trigger specifications, see also _trigger_builds.
+ timeout: if not None, a datetime.timedelta for the step timeout.
+ infra_step: if True, this is an infrastructure step. Failures will raise
+ InfraFailure instead of StepFailure.
+ stdout: Placeholder to put step stdout into. If used, stdout won't appear
+ in annotator's stdout (and |allow_subannotations| is ignored).
+ stderr: Placeholder to put step stderr into. If used, stderr won't appear
+ in annotator's stderr.
+ stdin: Placeholder to read step stdin from.
+ ok_ret (iter): set of return codes allowed. If the step process returns
+ something not on this list, it will raise a StepFailure (or
+ InfraFailure if infra_step is True). If omitted, {0} will be used.
+ step_test_data (func -> recipe_test_api.StepTestData): A factory which
+ returns a StepTestData object that will be used as the default test
+ data for this step. The recipe author can override/augment this object
+ in the GenTests function.
+ nest_level (int): the step's nesting level.
+
+ The optional "env" parameter provides optional overrides for environment
+ variables. Each value is % formatted with the entire existing os.environ. A
+ value of `None` will remove that envvar from the environ. e.g.
+
+ {
+ "envvar": "%(envvar)s;%(envvar2)s;extra",
+ "delete_this": None,
+ "static_value": "something",
+ }
+ """
+
+ _RENDER_WHITELIST=frozenset((
+ 'cmd',
+ ))
+
+ _RENDER_BLACKLIST=frozenset((
+ 'base_name',
+ 'nest_level',
+ 'ok_ret',
+ 'step_test_data',
+ ))
+
+ def __new__(cls, **kwargs):
+ for field in cls._fields:
+ kwargs.setdefault(field, None)
+ sc = super(StepClient.StepConfig, cls).__new__(cls, **kwargs)
+
+ return sc._replace(
+ cmd=[(x if isinstance(x, Placeholder) else str(x))
+ for x in (sc.cmd or ())],
+ cwd=(str(sc.cwd) if sc.cwd else (None)),
+ base_name=sc.base_name or sc.name,
+ allow_subannotations=bool(sc.allow_subannotations),
+ trigger_specs=sc.trigger_specs or (),
+ infra_step=bool(sc.infra_step),
+ ok_ret=frozenset(sc.ok_ret or (0,)),
+ nest_level=int(sc.nest_level or 0),
+ )
+
+ def render_to_dict(self):
+ sc = self._replace(
+ trigger_specs=[trig._render_to_dict()
+ for trig in (self.trigger_specs or ())],
+ )
+ return dict((k, v) for k, v in sc._asdict().iteritems()
+ if (v or k in sc._RENDER_WHITELIST)
+ and k not in sc._RENDER_BLACKLIST)
+
+
+ class TriggerSpec(collections.namedtuple('_TriggerSpec', (
+ 'bucket', 'builder_name', 'properties', 'buildbot_changes', 'tags',
+ 'critical'))):
+
+ """
+ TriggerSpec is the internal representation of a raw trigger step. You should
+ use the standard 'step' recipe module, which will construct trigger specs
+ via API.
+
+ Fields:
+ builder_name (str): The name of the builder to trigger.
+ bucket (str or None): The name of the trigger bucket.
+ properties (dict or None): Key/value properties dictionary.
+ buildbot_changes (list or None): Optional list of BuildBot change dicts.
+ tags (list or None): Optional list of tag strings.
+ critical (bool or None): If true and triggering fails asynchronously, fail
+ the entire build. If None, the step defaults to being True.
+ """
+
+ def __new__(cls, **kwargs):
+ for field in cls._fields:
+ kwargs.setdefault(field, None)
+ trig = super(StepClient.TriggerSpec, cls).__new__(cls, **kwargs)
+ return trig._replace(
+ critical=bool(trig.critical),
+ )
+
+ def _render_to_dict(self):
+ d = dict((k, v) for k, v in self._asdict().iteritems() if v)
+ if d['critical']:
+ d.pop('critical')
+ return d
+
+
def __init__(self, engine):
self._engine = engine
@@ -186,17 +311,18 @@ class StepClient(object):
'result.')
return self._engine._step_stack[-1].step_result
- def run_step(self, step_dict):
+ def run_step(self, step_config):
"""
- Runs a step.
+ Runs a step from a StepConfig.
Args:
- step_dict (dict): A step dictionary to run.
+ step_config: Keyword arguments to use to instantiate a StepConfig.
Returns:
A StepData object containing the result of running the step.
"""
- return self._engine.run_step(StepConfig.create(**step_dict))
+ assert isinstance(step_config, self.StepConfig)
+ return self._engine.run_step(step_config)
class DependencyManagerClient(object):
@@ -211,152 +337,6 @@ class DependencyManagerClient(object):
return self._engine.depend_on(recipe, properties, **kwargs)
-_TriggerSpec = collections.namedtuple('_TriggerSpec',
- ('bucket', 'builder_name', 'properties', 'buildbot_changes', 'tags',
- 'critical'))
-
-class TriggerSpec(_TriggerSpec):
- """
- TriggerSpec is the internal representation of a raw trigger step. You should
- use the standard 'step' recipe module, which will construct trigger specs
- via API.
- """
-
- @classmethod
- def _create(cls, builder_name, bucket=None, properties=None,
- buildbot_changes=None, tags=None, critical=None):
- """Creates a new TriggerSpec instance from its step API dictionary
- keys/values.
-
- Args:
- builder_name (str): The name of the builder to trigger.
- bucket (str or None): The name of the trigger bucket.
- properties (dict or None): Key/value properties dictionary.
- buildbot_changes (list or None): Optional list of BuildBot change dicts.
- tags (list or None): Optional list of tag strings.
- critical (bool or None): If true and triggering fails asynchronously, fail
- the entire build. If None, the step defaults to being True.
- """
- if not isinstance(buildbot_changes, (types.NoneType, list)):
- raise ValueError('buildbot_changes must be a list')
-
- return cls(
- bucket=bucket,
- builder_name=builder_name,
- properties=properties,
- buildbot_changes=buildbot_changes,
- tags=tags,
- critical=bool(critical) if critical is not None else (True),
- )
-
- def _render_to_dict(self):
- d = dict((k, v) for k, v in self._asdict().iteritems() if v)
- if d['critical']:
- d.pop('critical')
- return d
-
-
-_StepConfig = collections.namedtuple('_StepConfig',
- ('name', 'base_name', 'cmd', 'cwd', 'env', 'allow_subannotations',
- 'trigger_specs', 'timeout', 'infra_step', 'stdout', 'stderr', 'stdin',
- 'ok_ret', 'step_test_data', 'nest_level'))
-
-class StepConfig(_StepConfig):
- """
- StepConfig is the representation of a raw step as the recipe_engine sees it.
- You should use the standard 'step' recipe module, which will construct and
- pass this data to the engine for you, instead. The only reason why you would
- need to worry about this object is if you're modifying the step module itself.
-
- The optional "env" parameter provides optional overrides for environment
- variables. Each value is % formatted with the entire existing os.environ. A
- value of `None` will remove that envvar from the environ. e.g.
-
- {
- "envvar": "%(envvar)s;%(envvar2)s;extra",
- "delete_this": None,
- "static_value": "something",
- }
- """
-
- _RENDER_WHITELIST=frozenset((
- 'cmd',
- ))
-
- _RENDER_BLACKLIST=frozenset((
- 'base_name',
- 'nest_level',
- 'ok_ret',
- 'step_test_data',
- ))
-
- @classmethod
- def create(cls, name, base_name=None, cmd=None, cwd=None, env=None,
- allow_subannotations=None, trigger_specs=None, timeout=None,
- infra_step=None, stdout=None, stderr=None, stdin=None,
- ok_ret=None, step_test_data=None, step_nest_level=None):
- """
- Initializes a new StepConfig step API dictionary.
-
- Args:
- name (str): name of the step, will appear in buildbots waterfall
- base_name (str): the base name of the step. If the step has a derived
- name (e.g., nested may be concatenated with its parent), this is the
- name component of just this step. If None, this will be set to "name".
- cmd: command to run. Acceptable types: str, Path, Placeholder, or None.
- cwd (str or None): absolute path to working directory for the command
- env (dict): overrides for environment variables, described above.
- allow_subannotations (bool): if True, lets the step emit its own
- annotations. NOTE: Enabling this can cause some buggy behavior. Please
- strongly consider using step_result.presentation instead. If you have
- questions, please contact infra-dev@chromium.org.
- trigger_specs: a list of trigger specifications, see also _trigger_builds.
- timeout: if not None, a datetime.timedelta for the step timeout.
- infra_step: if True, this is an infrastructure step. Failures will raise
- InfraFailure instead of StepFailure.
- stdout: Placeholder to put step stdout into. If used, stdout won't appear
- in annotator's stdout (and |allow_subannotations| is ignored).
- stderr: Placeholder to put step stderr into. If used, stderr won't appear
- in annotator's stderr.
- stdin: Placeholder to read step stdin from.
- ok_ret (iter): set of return codes allowed. If the step process returns
- something not on this list, it will raise a StepFailure (or
- InfraFailure if infra_step is True). If omitted, {0} will be used.
- step_test_data (func -> recipe_test_api.StepTestData): A factory which
- returns a StepTestData object that will be used as the default test
- data for this step. The recipe author can override/augment this object
- in the GenTests function.
- step_nest_level (int): the step's nesting level.
- """
- return cls(
- name=name,
- base_name=(base_name or name),
- cmd=cmd,
- cwd=cwd,
- env=env,
- allow_subannotations=bool(allow_subannotations),
- trigger_specs=[TriggerSpec._create(**trig)
- for trig in (trigger_specs or ())],
- timeout=timeout,
- infra_step=bool(infra_step),
- stdout=stdout,
- stderr=stderr,
- stdin=stdin,
- ok_ret=frozenset(ok_ret or (0,)),
- step_test_data=step_test_data,
- nest_level=int(step_nest_level or 0),
- )
-
- def render_to_dict(self):
- self = self._replace(
- trigger_specs=[trig._render_to_dict()
- for trig in (self.trigger_specs or ())],
- )
- return dict((k, v) for k, v in self._asdict().iteritems()
- if (v or k in self._RENDER_WHITELIST)
- and k not in self._RENDER_BLACKLIST)
-
-
class StepFailure(Exception):
"""
This is the base class for all step failures.
« no previous file with comments | « no previous file | recipe_engine/run.py » ('j') | recipe_modules/step/api.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698