| Index: scripts/slave/recipe_configs_util.py
|
| diff --git a/scripts/slave/recipe_configs_util.py b/scripts/slave/recipe_configs_util.py
|
| deleted file mode 100644
|
| index 4b824e6e54fcaf28fe95269438ad3796b14b3023..0000000000000000000000000000000000000000
|
| --- a/scripts/slave/recipe_configs_util.py
|
| +++ /dev/null
|
| @@ -1,707 +0,0 @@
|
| -# Copyright 2013 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).
|
| -
|
| -Once you have your schema, you define some testing data:
|
| - TEST_MAP = {
|
| - 'MAIN_DETERMINANT': (True, False),
|
| - 'CONFIG_MODE': ('Happy', 'Sad'),
|
| - }
|
| - TEST_NAME_FORMAT = '%(MAIN_DETERMINANT)s-%(CONFIG_MODE)s'
|
| -
|
| -The test map tells the test harness what parameters it should instantiate the
|
| -schema with, and what values those parameters should take. The test harness will
|
| -generate all possible permutations of input parameters, and will save them to
|
| -disk.
|
| -
|
| -The test format is a string format (or a function taking a dictionary of
|
| -variable assignments) which will be used to name the test files
|
| -and test cases for this configuration.
|
| -
|
| -Once you have all that, you can create a configuration context:
|
| -
|
| - config_ctx = config_item_context(FakeSchema, TEST_MAP, TEST_NAME_FORMAT)
|
| -
|
| -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
|
| -
|
| -class BadConf(Exception):
|
| - pass
|
| -
|
| -def config_item_context(CONFIG_SCHEMA, VAR_TEST_MAP, TEST_NAME_FORMAT,
|
| - TEST_FILE_FORMAT=None):
|
| - """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.
|
| - VAR_TEST_MAP: A dict mapping arg_name to an iterable of values. This
|
| - provides the test harness with sufficient information to
|
| - generate all possible permutations of inputs for the
|
| - CONFIG_SCHEMA function.
|
| - TEST_NAME_FORMAT: A string format (or function) for naming tests and test
|
| - expectation files. It will be formatted/called with a
|
| - dictionary of arg_name to value (using arg_names and
|
| - values generated from VAR_TEST_MAP)
|
| - TEST_FILE_FORMAT: Similar to TEST_NAME_FORMAT, but for test files. Defaults
|
| - to TEST_NAME_FORMAT.
|
| -
|
| - Returns a config_ctx decorator for this context.
|
| - """
|
| -
|
| - def config_ctx(group=None, includes=None, deps=None, no_test=False,
|
| - is_root=False):
|
| - """
|
| - 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 recieve 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.
|
| -
|
| - no_test(bool) - If set to True, then this config_ctx will be skipped by
|
| - the test harness. This defaults to (False or bool(deps)), since
|
| - config_items with deps will never be satisfiable as the first
|
| - config_ctx applied to a blob.
|
| -
|
| - 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.
|
| -
|
| - Additionally, the test harness uses the root item to probe for invalid
|
| - configuration combinations by running the root item first (if there is
|
| - one), and skipping the configuration combination if the root config
|
| - item throws BadConf.
|
| -
|
| - 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 = config_ctx.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 (config_ctx.ROOT_CONFIG_ITEM and not inner.IS_ROOT and
|
| - config_ctx.ROOT_CONFIG_ITEM.__name__ not in inclusions):
|
| - config_ctx.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:
|
| - config_ctx.CONFIG_ITEMS[include](config)
|
| - except BadConf, 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 & config_ctx.MUTEX_GROUPS[dep_group]):
|
| - raise BadConf('dep group "%s" is unfulfilled for "%s"' %
|
| - (dep_group, name))
|
| -
|
| - if group:
|
| - overlap = inclusions & config_ctx.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
|
| -
|
| - assert name not in config_ctx.CONFIG_ITEMS
|
| - config_ctx.CONFIG_ITEMS[name] = inner
|
| - if group:
|
| - config_ctx.MUTEX_GROUPS.setdefault(group, set()).add(name)
|
| - inner.IS_ROOT = is_root
|
| - if is_root:
|
| - assert not config_ctx.ROOT_CONFIG_ITEM, (
|
| - 'may only have one root config_ctx!')
|
| - config_ctx.ROOT_CONFIG_ITEM = inner
|
| - inner.IS_ROOT = True
|
| - inner.NO_TEST = no_test or bool(deps)
|
| - return inner
|
| - return decorator
|
| -
|
| - # Internal state and testing data
|
| - config_ctx.I_AM_A_CONFIG_CTX = True
|
| - config_ctx.CONFIG_ITEMS = {}
|
| - config_ctx.MUTEX_GROUPS = {}
|
| - config_ctx.CONFIG_SCHEMA = CONFIG_SCHEMA
|
| - config_ctx.ROOT_CONFIG_ITEM = None
|
| - config_ctx.VAR_TEST_MAP = VAR_TEST_MAP
|
| -
|
| - def formatter(obj, ext=None):
|
| - '''Converts format obj to a function taking var assignments.
|
| -
|
| - Args:
|
| - obj (str or fn(assignments)): If obj is a str, it will be % formatted
|
| - with assignments (which is a dict of variables from VAR_TEST_MAP).
|
| - Otherwise obj will be invoked with assignments, and expected to return
|
| - a fully-rendered string.
|
| - ext (None or str): Optionally specify an extension to enforce on the
|
| - format. This enforcement occurs after obj is finalized to a string. If
|
| - the string doesn't end with ext, it will be appended.
|
| - '''
|
| - def inner(var_assignments):
|
| - ret = ''
|
| - if isinstance(obj, basestring):
|
| - ret = obj % var_assignments
|
| - else:
|
| - ret = obj(var_assignments)
|
| - if ext and not ret.endswith(ext):
|
| - ret += ext
|
| - return ret
|
| - return inner
|
| - config_ctx.TEST_NAME_FORMAT = formatter(TEST_NAME_FORMAT)
|
| - config_ctx.TEST_FILE_FORMAT = formatter(
|
| - (TEST_FILE_FORMAT or TEST_NAME_FORMAT), ext='.json')
|
| - return config_ctx
|
| -
|
| -
|
| -class ConfigBase(object):
|
| - """This is the root interface for all config schema types."""
|
| -
|
| - def __init__(self, hidden=False):
|
| - """
|
| - Args:
|
| - hidden - If set to 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.
|
| - """
|
| - # work around subclasses which override __setattr__
|
| - object.__setattr__(self, '_hidden', 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
|
| -
|
| -
|
| -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=False, **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():
|
| - assert isinstance(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)
|
| - assert isinstance(obj, ConfigBase)
|
| - obj.set_val(val)
|
| -
|
| - def __delattr__(self, name):
|
| - obj = object.__getattribute__(self, name)
|
| - assert isinstance(obj, ConfigBase)
|
| - obj.reset()
|
| -
|
| - def set_val(self, val):
|
| - if isinstance(val, ConfigBase):
|
| - val = val.as_jsonish(include_hidden=True)
|
| - assert isinstance(val, dict)
|
| - for name, config_obj in self._type_map.iteritems():
|
| - if name in val:
|
| - config_obj.set_val(val.pop())
|
| - assert not val, "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())
|
| -
|
| -
|
| -class ConfigList(ConfigBase, collections.MutableSequence):
|
| - """Allows you to provide an ordered repetition to a configuration schema.
|
| -
|
| - Example usage:
|
| - config_blob = ConfigGroup(
|
| - some_items = ConfigList(
|
| - 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=False):
|
| - """
|
| - Args:
|
| - item_schema: The schema of each object. Should be a function which returns
|
| - an instance of ConfigGroup.
|
| - """
|
| - super(ConfigList, self).__init__(hidden=hidden)
|
| - assert isinstance(item_schema, types.FunctionType)
|
| - assert isinstance(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)
|
| - assert isinstance(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
|
| -
|
| -
|
| -class DictConfig(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=False):
|
| - """
|
| - 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(DictConfig, 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:
|
| - assert isinstance(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, DictConfig):
|
| - val = val.data
|
| - assert isinstance(val, dict)
|
| - assert all(isinstance(v, self.value_type) for v in val.itervalues())
|
| - 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
|
| -
|
| -
|
| -class ListConfig(ConfigBase, collections.MutableSequence):
|
| - """Provides a semi-homogenous list()-like configuration object."""
|
| -
|
| - def __init__(self, inner_type, jsonish_fn=list, hidden=False):
|
| - """
|
| - 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(ListConfig, 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):
|
| - assert isinstance(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):
|
| - assert isinstance(value, self.inner_type)
|
| - self.data.insert(index, value)
|
| -
|
| - def set_val(self, val):
|
| - assert all(isinstance(v, self.inner_type) for v in val)
|
| - 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
|
| -
|
| -
|
| -class SetConfig(ConfigBase, collections.MutableSet):
|
| - """Provides a semi-homogenous set()-like configuration object."""
|
| -
|
| - def __init__(self, inner_type, jsonish_fn=list, hidden=False):
|
| - """
|
| - 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(SetConfig, 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):
|
| - assert isinstance(value, self.inner_type)
|
| - self.data.add(value)
|
| -
|
| - def discard(self, value):
|
| - self.data.discard(value)
|
| -
|
| - def set_val(self, val):
|
| - assert all(isinstance(v, self.inner_type) for v in val)
|
| - 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
|
| -
|
| -
|
| -class SimpleConfig(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=False):
|
| - """
|
| - 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.
|
| - emtpy_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(SimpleConfig, 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, SimpleConfig):
|
| - val = val.data
|
| - assert val is self.empty_val or isinstance(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
|
| -
|
| -
|
| -class StaticConfig(ConfigBase):
|
| - """Holds a single, hidden, immutible data object.
|
| -
|
| - This is very useful for holding the 'input' configuration values (i.e. those
|
| - which are in your VAR_TEST_MAP).
|
| - """
|
| -
|
| - def __init__(self, value, hidden=True):
|
| - super(StaticConfig, 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):
|
| - assert False
|
| -
|
| - def as_jsonish(self, _include_hidden=None):
|
| - return self.data
|
| -
|
| - def reset(self):
|
| - assert False
|
| -
|
| - def complete(self):
|
| - return True
|
|
|