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

Unified Diff: third_party/recipe_engine/config.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Roll to latest recipes-py Created 5 years, 3 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 | « third_party/recipe_engine/__init__.py ('k') | third_party/recipe_engine/config_types.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/recipe_engine/config.py
diff --git a/third_party/recipe_engine/config.py b/third_party/recipe_engine/config.py
deleted file mode 100644
index 693b129670fe34442fbcb407598320b61c84d4e4..0000000000000000000000000000000000000000
--- a/third_party/recipe_engine/config.py
+++ /dev/null
@@ -1,736 +0,0 @@
-# Copyright 2013-2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Recipe Configuration Meta DSL.
-
-This module contains, essentially, a DSL for writing composable configurations.
-You start by defining a schema which describes how your configuration blobs will
-be structured, and what data they can contain. For example:
-
- FakeSchema = lambda main_val=True, mode='Happy': ConfigGroup(
- config_group = ConfigGroup(
- item_a = SimpleConfig(int),
- item_b = DictConfig(),
- ),
- extra_setting = SetConfig(str),
-
- MAIN_DETERMINANT = StaticConfig(main_val),
- CONFIG_MODE = StaticConfig(mode),
- )
-
-In short, a 'schema' is a callable which can take zero arguments (it can contain
-default arguments as well, for setting defaults, tweaking the schema, etc.), and
-returning a ConfigGroup.
-
-Every type used in the schema derives from ConfigBase. It has the general
-characteristics that it's a fixed-type container. It tends to impersonate the
-data type that it stores (so you can manipulate the config objects like normal
-python data), but also provides type checking and type conversion assistence
-(so you can easily render your configurations to JSON).
-
-Then you can create a configuration context:
-
- config_ctx = config_item_context(FakeSchema)
-
-config_ctx is a python decorator which you can use to create composable
-configuration functions. For example:
-
- @config_ctx()
- def cool(c):
- if c.CONFIG_MODE == 'Happy':
- c.config_group.item_a = 100
- else:
- c.config_group.item_a = -100
-
- @config_ctx()
- def gnarly(c):
- c.extra_setting = 'gnarly!'
-
- @config_ctx(includes=('cool', 'gnarly'))
- def combo(c):
- if c.MAIN_DETERMINANT:
- c.config_group.item_b['nickname'] = 'purple'
- c.extra_setting += ' cows!'
- else:
- c.config_group.item_b['nickname'] = 'sad times'
-
-If I now call:
-
- combo()
-
-I will get back a configuraton object whose schema is FakeSchema, and whose
-data is the accumulation of cool(), gnarly(), and combo(). I can continue to
-manipulate this configuraton object, use its data, or render it to json.
-
-Using this system should allow you to create rich, composible,
-modular configurations. See the documentation on config_item_context and the
-BaseConfig derivatives for more info.
-"""
-
-import collections
-import functools
-import types
-
-from infra.libs import infra_types
-
-class BadConf(Exception):
- pass
-
-def typeAssert(obj, typearg):
- if not isinstance(obj, typearg):
- raise TypeError("Expected %r to be of type %r" % (obj, typearg))
-
-
-class ConfigContext(object):
- """A configuration context for a recipe module.
-
- Holds configuration schema and also acts as a config_ctx decorator.
- A recipe module can define at most one such context.
- """
-
- def __init__(self, CONFIG_SCHEMA):
- self.CONFIG_ITEMS = {}
- self.MUTEX_GROUPS = {}
- self.CONFIG_SCHEMA = CONFIG_SCHEMA
- self.ROOT_CONFIG_ITEM = None
-
- def __call__(self, group=None, includes=None, deps=None,
- is_root=False, config_vars=None):
- """
- A decorator for functions which modify a given schema of configs.
- Examples continue using the schema and config_items defined in the module
- docstring.
-
- This decorator provides a series of related functions:
- * Any function decorated with this will be registered into this config
- context by __name__. This enables some of the other following features
- to work.
- * Alters the signature of the function so that it can receive an extra
- parameter 'final'. See the documentation for final on inner().
- * Provides various convenience and error checking facilities.
- * In particular, this decorator will prevent you from calling the same
- config_ctx on a given config blob more than once (with the exception
- of setting final=False. See inner())
-
- Args:
- group(str) - Using this decorator with the `group' argument causes the
- decorated function to be a member of that group. Members of a group are
- mutually exclusive on the same configuration blob. For example, only
- one of these two functions could be applied to the config blob c:
- @config_ctx(group='a')
- def bob(c):
- c.extra_setting = "bob mode"
-
- @config_ctx(group='a')
- def bill(c):
- c.extra_setting = "bill mode"
-
- includes(iterable(str)) - Any config items named in the includes list will
- be run against the config blob before the decorated function can modify
- it. If an inclusion is already applied to the config blob, it's skipped
- without applying/raising BadConf. Example:
- @config_ctx(includes=('bob', 'cool'))
- def charlie(c):
- c.config_group.item_b = 25
- The result of this config_ctx (assuming default values for the schema)
- would be:
- {'config_group': { 'item_a': 100, 'item_b': 25 },
- 'extra_setting': 'gnarly!'}
-
- deps(iterable(str)) - One or more groups which must be satisfied before
- this config_ctx can be applied to a config_blob. If you invoke
- a config_ctx on a blob without having all of its deps satisfied,
- you'll get a BadConf exception.
-
- is_root(bool) - If set to True on an item, this item will become the
- 'basis' item for all other configurations in this group. That means that
- it will be implicitly included in all other config_items. There may only
- ever be one root item.
-
- config_vars(dict) - A dictionary mapping of { CONFIG_VAR: <value> }. This
- sets the input contidions for the CONFIG_SCHEMA.
-
- Returns a new decorated version of this function (see inner()).
- """
- def decorator(f):
- name = f.__name__
- @functools.wraps(f)
- def inner(config=None, final=True, optional=False, **kwargs):
- """This is the function which is returned from the config_ctx
- decorator.
-
- It applies all of the logic mentioned in the config_ctx docstring
- above, and alters the function signature slightly.
-
- Args:
- config - The config blob that we intend to manipulate. This is passed
- through to the function after checking deps and including includes.
- After the function manipulates it, it is automatically returned.
-
- final(bool) - Set to True by default, this will record the application
- of this config_ctx to `config', which will prevent the config_ctx
- from being applied to `config' again. It also is used to see if the
- config blob satisfies deps for subsequent config_ctx applications
- (i.e. in order for a config_ctx to satisfy a dependency, it must
- be applied with final=True).
-
- This is useful to apply default values while allowing the config to
- later override those values.
-
- However, it's best if each config_ctx is final, because then you
- can implement the config items with less error checking, since you
- know that the item may only be applied once. For example, if your
- item appends something to a list, but is called with final=False,
- you'll have to make sure that you don't append the item twice, etc.
-
- **kwargs - Passed through to the decorated function without harm.
-
- Returns config and ignores the return value of the decorated function.
- """
- if config is None:
- config = self.CONFIG_SCHEMA()
- assert isinstance(config, ConfigGroup)
- inclusions = config._inclusions # pylint: disable=W0212
-
- # inner.IS_ROOT will be True or False at the time of invocation.
- if (self.ROOT_CONFIG_ITEM and not inner.IS_ROOT and
- self.ROOT_CONFIG_ITEM.__name__ not in inclusions):
- self.ROOT_CONFIG_ITEM(config)
-
- if name in inclusions:
- if optional:
- return config
- raise BadConf('config_ctx "%s" is already in this config "%s"' %
- (name, config.as_jsonish(include_hidden=True)))
- if final:
- inclusions.add(name)
-
- for include in includes or []:
- if include in inclusions:
- continue
- try:
- self.CONFIG_ITEMS[include](config)
- except BadConf as e:
- raise BadConf('config "%s" includes "%s", but [%s]' %
- (name, include, e))
-
- # deps are a list of group names. All groups must be represented
- # in config already.
- for dep_group in deps or []:
- if not inclusions & self.MUTEX_GROUPS[dep_group]:
- raise BadConf('dep group "%s" is unfulfilled for "%s"' %
- (dep_group, name))
-
- if group:
- overlap = inclusions & self.MUTEX_GROUPS[group]
- overlap.discard(name)
- if overlap:
- raise BadConf('"%s" is a member of group "%s", but %s already ran' %
- (name, group, tuple(overlap)))
-
- ret = f(config, **kwargs)
- assert ret is None, 'Got return value (%s) from "%s"?' % (ret, name)
-
- return config
- inner.WRAPPED = f
- inner.INCLUDES = includes or []
-
- def default_config_vars():
- ret = {}
- for include in includes or []:
- item = self.CONFIG_ITEMS[include]
- ret.update(item.DEFAULT_CONFIG_VARS())
- if config_vars:
- ret.update(config_vars)
- return ret
- inner.DEFAULT_CONFIG_VARS = default_config_vars
-
- assert name not in self.CONFIG_ITEMS, (
- '%s is already in CONFIG_ITEMS' % name)
- self.CONFIG_ITEMS[name] = inner
- if group:
- self.MUTEX_GROUPS.setdefault(group, set()).add(name)
- inner.IS_ROOT = is_root
- if is_root:
- assert not self.ROOT_CONFIG_ITEM, (
- 'may only have one root config_ctx!')
- self.ROOT_CONFIG_ITEM = inner
- inner.IS_ROOT = True
- return inner
- return decorator
-
-
-def config_item_context(CONFIG_SCHEMA):
- """Create a configuration context.
-
- Args:
- CONFIG_SCHEMA: This is a function which can take a minimum of zero arguments
- and returns an instance of BaseConfig. This BaseConfig
- defines the schema for all configuration objects manipulated
- in this context.
-
- Returns a config_ctx decorator for this context.
- """
- return ConfigContext(CONFIG_SCHEMA)
-
-
-class AutoHide(object):
- pass
-AutoHide = AutoHide()
-
-
-class ConfigBase(object):
- """This is the root interface for all config schema types."""
-
- def __init__(self, hidden=AutoHide):
- """
- Args:
- hidden -
- True: This object will be excluded from printing when the config blob
- is rendered with ConfigGroup.as_jsonish(). You still have full
- read/write access to this blob otherwise though.
- False: This will be printed as part of ConfigGroup.as_jsonish()
- AutoHide: This will be printed as part of ConfigGroup.as_jsonish() only
- if self._is_default() is false.
- """
- # work around subclasses which override __setattr__
- object.__setattr__(self, '_hidden_mode', hidden)
- object.__setattr__(self, '_inclusions', set())
-
- def get_val(self):
- """Gets the native value of this config object."""
- return self
-
- def set_val(self, val):
- """Resets the value of this config object using data in val."""
- raise NotImplementedError
-
- def reset(self):
- """Resets the value of this config object to it's initial state."""
- raise NotImplementedError
-
- def as_jsonish(self, include_hidden=False):
- """Returns the value of this config object as simple types."""
- raise NotImplementedError
-
- def complete(self):
- """Returns True iff this configuraton blob is fully viable."""
- raise NotImplementedError
-
- def _is_default(self):
- """Returns True iff this configuraton blob is the default value."""
- raise NotImplementedError
-
- @property
- def _hidden(self):
- """Returns True iff this configuraton blob is hidden."""
- if self._hidden_mode is AutoHide:
- return self._is_default()
- return self._hidden_mode
-
-
-class ConfigGroup(ConfigBase):
- """Allows you to provide hierarchy to a configuration schema.
-
- Example usage:
- config_blob = ConfigGroup(
- some_item = SimpleConfig(str),
- group = ConfigGroup(
- numbahs = SetConfig(int),
- ),
- )
- config_blob.some_item = "hello"
- config_blob.group.numbahs.update(range(10))
- """
-
- def __init__(self, hidden=AutoHide, **type_map):
- """Expects type_map to be {python_name -> ConfigBase} instance."""
- super(ConfigGroup, self).__init__(hidden)
- assert type_map, 'A ConfigGroup with no type_map is meaningless.'
-
- object.__setattr__(self, '_type_map', type_map)
- for name, typeval in self._type_map.iteritems():
- typeAssert(typeval, ConfigBase)
- object.__setattr__(self, name, typeval)
-
- def __getattribute__(self, name):
- obj = object.__getattribute__(self, name)
- if isinstance(obj, ConfigBase):
- return obj.get_val()
- else:
- return obj
-
- def __setattr__(self, name, val):
- obj = object.__getattribute__(self, name)
- typeAssert(obj, ConfigBase)
- obj.set_val(val)
-
- def __delattr__(self, name):
- obj = object.__getattribute__(self, name)
- typeAssert(obj, ConfigBase)
- obj.reset()
-
- def set_val(self, val):
- if isinstance(val, ConfigBase):
- val = val.as_jsonish(include_hidden=True)
- if isinstance(val, infra_types.FrozenDict):
- val = infra_types.thaw(val)
- typeAssert(val, dict)
-
- val = dict(val) # because we pop later.
- for name, config_obj in self._type_map.iteritems():
- if name in val:
- try:
- config_obj.set_val(val.pop(name))
- except Exception as e:
- raise Exception('While assigning key %r: %s' % (name, e))
-
- if val:
- raise TypeError("Got extra keys while setting ConfigGroup: %s" % val)
-
- def as_jsonish(self, include_hidden=False):
- return dict(
- (n, v.as_jsonish(include_hidden)) for n, v in self._type_map.iteritems()
- if include_hidden or not v._hidden) # pylint: disable=W0212
-
- def reset(self):
- for v in self._type_map.values():
- v.reset()
-
- def complete(self):
- return all(v.complete() for v in self._type_map.values())
-
- def _is_default(self):
- # pylint: disable=W0212
- return all(v._is_default() for v in self._type_map.values())
-
-
-class ConfigList(ConfigBase, collections.MutableSequence):
- """Allows you to provide an ordered repetition to a configuration schema.
-
- Example usage:
- config_blob = ConfigGroup(
- some_items = ConfigList(
- lambda: ConfigGroup(
- herp = SimpleConfig(int),
- derp = SimpleConfig(str)
- )
- )
- )
- config_blob.some_items.append({'herp': 1})
- config_blob.some_items[0].derp = 'bob'
- """
-
- def __init__(self, item_schema, hidden=AutoHide):
- """
- Args:
- item_schema: The schema of each object. Should be a function which returns
- an instance of ConfigGroup.
- """
- super(ConfigList, self).__init__(hidden=hidden)
- typeAssert(item_schema, types.FunctionType)
- typeAssert(item_schema(), ConfigGroup)
- self.item_schema = item_schema
- self.data = []
-
- def __getitem__(self, index):
- return self.data.__getitem__(index)
-
- def __setitem__(self, index, value):
- datum = self.item_schema()
- datum.set_val(value)
- return self.data.__setitem__(index, datum)
-
- def __delitem__(self, index):
- return self.data.__delitem__(index)
-
- def __len__(self):
- return len(self.data)
-
- def insert(self, index, value):
- datum = self.item_schema()
- datum.set_val(value)
- return self.data.insert(index, datum)
-
- def add(self):
- self.append({})
- return self[-1]
-
- def reset(self):
- self.data = []
-
- def complete(self):
- return all(i.complete() for i in self.data)
-
- def set_val(self, data):
- if isinstance(data, ConfigList):
- data = data.as_jsonish(include_hidden=True)
-
- typeAssert(data, list)
- self.reset()
- for item in data:
- self.append(item)
-
- def as_jsonish(self, include_hidden=False):
- return [i.as_jsonish(include_hidden) for i in self.data
- if include_hidden or not i._hidden] # pylint: disable=W0212
-
- def _is_default(self):
- # pylint: disable=W0212
- return all(v._is_default() for v in self.data)
-
-
-class Dict(ConfigBase, collections.MutableMapping):
- """Provides a semi-homogenous dict()-like configuration object."""
-
- def __init__(self, item_fn=lambda i: i, jsonish_fn=dict, value_type=None,
- hidden=AutoHide):
- """
- Args:
- item_fn - A function which renders (k, v) pairs to input items for
- jsonish_fn. Defaults to the identity function.
- jsonish_fn - A function which renders a list of outputs of item_fn to a
- JSON-compatiple python datatype. Defaults to dict().
- value_type - A type object used for constraining/validating the values
- assigned to this dictionary.
- hidden - See ConfigBase.
- """
- super(Dict, self).__init__(hidden)
- self.value_type = value_type
- self.item_fn = item_fn
- self.jsonish_fn = jsonish_fn
- self.data = {}
-
- def __getitem__(self, k):
- return self.data.__getitem__(k)
-
- def __setitem__(self, k, v):
- if self.value_type:
- typeAssert(v, self.value_type)
- return self.data.__setitem__(k, v)
-
- def __delitem__(self, k):
- return self.data.__delitem__(k)
-
- def __iter__(self):
- return iter(self.data)
-
- def __len__(self):
- return len(self.data)
-
- def set_val(self, val):
- if isinstance(val, Dict):
- val = val.data
- if isinstance(val, infra_types.FrozenDict):
- val = dict(val)
-
- typeAssert(val, dict)
- for v in val.itervalues():
- typeAssert(v, self.value_type)
- self.data = val
-
- def as_jsonish(self, _include_hidden=None):
- return self.jsonish_fn(map(
- self.item_fn, sorted(self.data.iteritems(), key=lambda x: x[0])))
-
- def reset(self):
- self.data.clear()
-
- def complete(self):
- return True
-
- def _is_default(self):
- return not self.data
-
-
-class List(ConfigBase, collections.MutableSequence):
- """Provides a semi-homogenous list()-like configuration object."""
-
- def __init__(self, inner_type, jsonish_fn=list, hidden=AutoHide):
- """
- Args:
- inner_type - The type of data contained in this set, e.g. str, int, ...
- Can also be a tuple of types to allow more than one type.
- jsonish_fn - A function used to reduce the list() to a JSON-compatible
- python datatype. Defaults to list().
- hidden - See ConfigBase.
- """
- super(List, self).__init__(hidden)
- self.inner_type = inner_type
- self.jsonish_fn = jsonish_fn
- self.data = []
-
- def __getitem__(self, index):
- return self.data[index]
-
- def __setitem__(self, index, value):
- typeAssert(value, self.inner_type)
- self.data[index] = value
-
- def __delitem__(self, index):
- del self.data
-
- def __len__(self):
- return len(self.data)
-
- def __radd__(self, other):
- if not isinstance(other, list):
- other = list(other)
- return other + self.data
-
- def insert(self, index, value):
- typeAssert(value, self.inner_type)
- self.data.insert(index, value)
-
- def set_val(self, val):
- for v in val:
- typeAssert(v, self.inner_type)
- self.data = list(val)
-
- def as_jsonish(self, _include_hidden=None):
- return self.jsonish_fn(self.data)
-
- def reset(self):
- self.data = []
-
- def complete(self):
- return True
-
- def _is_default(self):
- return not self.data
-
-
-class Set(ConfigBase, collections.MutableSet):
- """Provides a semi-homogenous set()-like configuration object."""
-
- def __init__(self, inner_type, jsonish_fn=list, hidden=AutoHide):
- """
- Args:
- inner_type - The type of data contained in this set, e.g. str, int, ...
- Can also be a tuple of types to allow more than one type.
- jsonish_fn - A function used to reduce the set() to a JSON-compatible
- python datatype. Defaults to list().
- hidden - See ConfigBase.
- """
- super(Set, self).__init__(hidden)
- self.inner_type = inner_type
- self.jsonish_fn = jsonish_fn
- self.data = set()
-
- def __contains__(self, val):
- return val in self.data
-
- def __iter__(self):
- return iter(self.data)
-
- def __len__(self):
- return len(self.data)
-
- def add(self, value):
- typeAssert(value, self.inner_type)
- self.data.add(value)
-
- def update(self, values):
- for value in values:
- if value not in self:
- self.add(value)
-
- def discard(self, value):
- self.data.discard(value)
-
- def set_val(self, val):
- for v in val:
- typeAssert(v, self.inner_type)
- self.data = set(val)
-
- def as_jsonish(self, _include_hidden=None):
- return self.jsonish_fn(sorted(self.data))
-
- def reset(self):
- self.data = set()
-
- def complete(self):
- return True
-
- def _is_default(self):
- return not self.data
-
-
-class Single(ConfigBase):
- """Provides a configuration object which holds a single 'simple' type."""
-
- def __init__(self, inner_type, jsonish_fn=lambda x: x, empty_val=None,
- required=True, hidden=AutoHide):
- """
- Args:
- inner_type - The type of data contained in this object, e.g. str, int, ...
- Can also be a tuple of types to allow more than one type.
- jsonish_fn - A function used to reduce the data to a JSON-compatible
- python datatype. Default is the identity function.
- empty_val - The value to use when initializing this object or when calling
- reset().
- required(bool) - True iff this config item is required to have a
- non-empty_val in order for it to be considered complete().
- hidden - See ConfigBase.
- """
- super(Single, self).__init__(hidden)
- self.inner_type = inner_type
- self.jsonish_fn = jsonish_fn
- self.empty_val = empty_val
- self.data = empty_val
- self.required = required
-
- def get_val(self):
- return self.data
-
- def set_val(self, val):
- if isinstance(val, Single):
- val = val.data
- if val is not self.empty_val:
- typeAssert(val, self.inner_type)
- self.data = val
-
- def as_jsonish(self, _include_hidden=None):
- return self.jsonish_fn(self.data)
-
- def reset(self):
- self.data = self.empty_val
-
- def complete(self):
- return not self.required or self.data is not self.empty_val
-
- def _is_default(self):
- return self.data is self.empty_val
-
-
-class Static(ConfigBase):
- """Holds a single, hidden, immutible data object.
-
- This is very useful for holding the 'input' configuration values.
- """
-
- def __init__(self, value, hidden=AutoHide):
- super(Static, self).__init__(hidden=hidden)
- # Attempt to hash the value, which will ensure that it's immutable all the
- # way down :).
- hash(value)
- self.data = value
-
- def get_val(self):
- return self.data
-
- def set_val(self, val):
- raise TypeError("Cannot assign to a Static config member")
-
- def as_jsonish(self, _include_hidden=None):
- return self.data
-
- def reset(self):
- assert False
-
- def complete(self):
- return True
-
- def _is_default(self):
- return True
« no previous file with comments | « third_party/recipe_engine/__init__.py ('k') | third_party/recipe_engine/config_types.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698