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

Side by Side Diff: scripts/slave/recipe_config.py

Issue 1151423002: Move recipe engine to third_party/recipe_engine. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved field_composer_test with its buddies Created 5 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « scripts/slave/recipe_api.py ('k') | scripts/slave/recipe_config_types.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Recipe Configuration Meta DSL.
6
7 This module contains, essentially, a DSL for writing composable configurations.
8 You start by defining a schema which describes how your configuration blobs will
9 be structured, and what data they can contain. For example:
10
11 FakeSchema = lambda main_val=True, mode='Happy': ConfigGroup(
12 config_group = ConfigGroup(
13 item_a = SimpleConfig(int),
14 item_b = DictConfig(),
15 ),
16 extra_setting = SetConfig(str),
17
18 MAIN_DETERMINANT = StaticConfig(main_val),
19 CONFIG_MODE = StaticConfig(mode),
20 )
21
22 In short, a 'schema' is a callable which can take zero arguments (it can contain
23 default arguments as well, for setting defaults, tweaking the schema, etc.), and
24 returning a ConfigGroup.
25
26 Every type used in the schema derives from ConfigBase. It has the general
27 characteristics that it's a fixed-type container. It tends to impersonate the
28 data type that it stores (so you can manipulate the config objects like normal
29 python data), but also provides type checking and type conversion assistence
30 (so you can easily render your configurations to JSON).
31
32 Once you have your schema, you define some testing data:
33 TEST_MAP = {
34 'MAIN_DETERMINANT': (True, False),
35 'CONFIG_MODE': ('Happy', 'Sad'),
36 }
37 TEST_NAME_FORMAT = '%(MAIN_DETERMINANT)s-%(CONFIG_MODE)s'
38
39 The test map tells the test harness what parameters it should instantiate the
40 schema with, and what values those parameters should take. The test harness will
41 generate all possible permutations of input parameters, and will save them to
42 disk.
43
44 The test format is a string format (or a function taking a dictionary of
45 variable assignments) which will be used to name the test files
46 and test cases for this configuration.
47
48 Once you have all that, you can create a configuration context:
49
50 config_ctx = config_item_context(FakeSchema, TEST_MAP, TEST_NAME_FORMAT)
51
52 config_ctx is a python decorator which you can use to create composable
53 configuration functions. For example:
54
55 @config_ctx()
56 def cool(c):
57 if c.CONFIG_MODE == 'Happy':
58 c.config_group.item_a = 100
59 else:
60 c.config_group.item_a = -100
61
62 @config_ctx()
63 def gnarly(c):
64 c.extra_setting = 'gnarly!'
65
66 @config_ctx(includes=('cool', 'gnarly'))
67 def combo(c):
68 if c.MAIN_DETERMINANT:
69 c.config_group.item_b['nickname'] = 'purple'
70 c.extra_setting += ' cows!'
71 else:
72 c.config_group.item_b['nickname'] = 'sad times'
73
74 If I now call:
75
76 combo()
77
78 I will get back a configuraton object whose schema is FakeSchema, and whose
79 data is the accumulation of cool(), gnarly(), and combo(). I can continue to
80 manipulate this configuraton object, use its data, or render it to json.
81
82 Using this system should allow you to create rich, composible,
83 modular configurations. See the documentation on config_item_context and the
84 BaseConfig derivatives for more info.
85 """
86
87 import collections
88 import functools
89 import types
90
91 from infra.libs import infra_types
92
93 class BadConf(Exception):
94 pass
95
96
97 class ConfigContext(object):
98 """A configuration context for a recipe module.
99
100 Holds configuration schema and also acts as a config_ctx decorator.
101 A recipe module can define at most one such context.
102 """
103
104 def __init__(self, CONFIG_SCHEMA, VAR_TEST_MAP, TEST_NAME_FORMAT):
105 self.CONFIG_ITEMS = {}
106 self.MUTEX_GROUPS = {}
107 self.CONFIG_SCHEMA = CONFIG_SCHEMA
108 self.ROOT_CONFIG_ITEM = None
109 self.VAR_TEST_MAP = VAR_TEST_MAP
110 self.TEST_NAME_FORMAT = create_formatter(TEST_NAME_FORMAT)
111
112 def __call__(self, group=None, includes=None, deps=None, no_test=False,
113 is_root=False, config_vars=None):
114 """
115 A decorator for functions which modify a given schema of configs.
116 Examples continue using the schema and config_items defined in the module
117 docstring.
118
119 This decorator provides a series of related functions:
120 * Any function decorated with this will be registered into this config
121 context by __name__. This enables some of the other following features
122 to work.
123 * Alters the signature of the function so that it can recieve an extra
124 parameter 'final'. See the documentation for final on inner().
125 * Provides various convenience and error checking facilities.
126 * In particular, this decorator will prevent you from calling the same
127 config_ctx on a given config blob more than once (with the exception
128 of setting final=False. See inner())
129
130 Args:
131 group(str) - Using this decorator with the `group' argument causes the
132 decorated function to be a member of that group. Members of a group are
133 mutually exclusive on the same configuration blob. For example, only
134 one of these two functions could be applied to the config blob c:
135 @config_ctx(group='a')
136 def bob(c):
137 c.extra_setting = "bob mode"
138
139 @config_ctx(group='a')
140 def bill(c):
141 c.extra_setting = "bill mode"
142
143 includes(iterable(str)) - Any config items named in the includes list will
144 be run against the config blob before the decorated function can modify
145 it. If an inclusion is already applied to the config blob, it's skipped
146 without applying/raising BadConf. Example:
147 @config_ctx(includes=('bob', 'cool'))
148 def charlie(c):
149 c.config_group.item_b = 25
150 The result of this config_ctx (assuming default values for the schema)
151 would be:
152 {'config_group': { 'item_a': 100, 'item_b': 25 },
153 'extra_setting': 'gnarly!'}
154
155 deps(iterable(str)) - One or more groups which must be satisfied before
156 this config_ctx can be applied to a config_blob. If you invoke
157 a config_ctx on a blob without having all of its deps satisfied,
158 you'll get a BadConf exception.
159
160 no_test(bool) - If set to True, then this config_ctx will be skipped by
161 the test harness. This defaults to (False or bool(deps)), since
162 config_items with deps will never be satisfiable as the first
163 config_ctx applied to a blob.
164
165 is_root(bool) - If set to True on an item, this item will become the
166 'basis' item for all other configurations in this group. That means that
167 it will be implicitly included in all other config_items. There may only
168 ever be one root item.
169
170 Additionally, the test harness uses the root item to probe for invalid
171 configuration combinations by running the root item first (if there is
172 one), and skipping the configuration combination if the root config
173 item throws BadConf.
174
175 config_vars(dict) - A dictionary mapping of { CONFIG_VAR: <value> }. This
176 sets the input contidions for the CONFIG_SCHEMA.
177
178 Returns a new decorated version of this function (see inner()).
179 """
180 def decorator(f):
181 name = f.__name__
182 @functools.wraps(f)
183 def inner(config=None, final=True, optional=False, **kwargs):
184 """This is the function which is returned from the config_ctx
185 decorator.
186
187 It applies all of the logic mentioned in the config_ctx docstring
188 above, and alters the function signature slightly.
189
190 Args:
191 config - The config blob that we intend to manipulate. This is passed
192 through to the function after checking deps and including includes.
193 After the function manipulates it, it is automatically returned.
194
195 final(bool) - Set to True by default, this will record the application
196 of this config_ctx to `config', which will prevent the config_ctx
197 from being applied to `config' again. It also is used to see if the
198 config blob satisfies deps for subsequent config_ctx applications
199 (i.e. in order for a config_ctx to satisfy a dependency, it must
200 be applied with final=True).
201
202 This is useful to apply default values while allowing the config to
203 later override those values.
204
205 However, it's best if each config_ctx is final, because then you
206 can implement the config items with less error checking, since you
207 know that the item may only be applied once. For example, if your
208 item appends something to a list, but is called with final=False,
209 you'll have to make sure that you don't append the item twice, etc.
210
211 **kwargs - Passed through to the decorated function without harm.
212
213 Returns config and ignores the return value of the decorated function.
214 """
215 if config is None:
216 config = self.CONFIG_SCHEMA()
217 assert isinstance(config, ConfigGroup)
218 inclusions = config._inclusions # pylint: disable=W0212
219
220 # inner.IS_ROOT will be True or False at the time of invocation.
221 if (self.ROOT_CONFIG_ITEM and not inner.IS_ROOT and
222 self.ROOT_CONFIG_ITEM.__name__ not in inclusions):
223 self.ROOT_CONFIG_ITEM(config)
224
225 if name in inclusions:
226 if optional:
227 return config
228 raise BadConf('config_ctx "%s" is already in this config "%s"' %
229 (name, config.as_jsonish(include_hidden=True)))
230 if final:
231 inclusions.add(name)
232
233 for include in includes or []:
234 if include in inclusions:
235 continue
236 try:
237 self.CONFIG_ITEMS[include](config)
238 except BadConf as e:
239 raise BadConf('config "%s" includes "%s", but [%s]' %
240 (name, include, e))
241
242 # deps are a list of group names. All groups must be represented
243 # in config already.
244 for dep_group in deps or []:
245 if not inclusions & self.MUTEX_GROUPS[dep_group]:
246 raise BadConf('dep group "%s" is unfulfilled for "%s"' %
247 (dep_group, name))
248
249 if group:
250 overlap = inclusions & self.MUTEX_GROUPS[group]
251 overlap.discard(name)
252 if overlap:
253 raise BadConf('"%s" is a member of group "%s", but %s already ran' %
254 (name, group, tuple(overlap)))
255
256 ret = f(config, **kwargs)
257 assert ret is None, 'Got return value (%s) from "%s"?' % (ret, name)
258
259 return config
260 inner.WRAPPED = f
261 inner.INCLUDES = includes or []
262
263 def default_config_vars():
264 ret = {}
265 for include in includes or []:
266 item = self.CONFIG_ITEMS[include]
267 ret.update(item.DEFAULT_CONFIG_VARS())
268 if config_vars:
269 ret.update(config_vars)
270 return ret
271 inner.DEFAULT_CONFIG_VARS = default_config_vars
272
273 assert name not in self.CONFIG_ITEMS
274 self.CONFIG_ITEMS[name] = inner
275 if group:
276 self.MUTEX_GROUPS.setdefault(group, set()).add(name)
277 inner.IS_ROOT = is_root
278 if is_root:
279 assert not self.ROOT_CONFIG_ITEM, (
280 'may only have one root config_ctx!')
281 self.ROOT_CONFIG_ITEM = inner
282 inner.IS_ROOT = True
283 inner.NO_TEST = no_test or bool(deps)
284 return inner
285 return decorator
286
287
288 def create_formatter(obj, ext=None):
289 """Converts format obj to a function taking var assignments.
290
291 Args:
292 obj (str or fn(assignments)): If obj is a str, it will be % formatted
293 with assignments (which is a dict of variables from VAR_TEST_MAP).
294 Otherwise obj will be invoked with assignments, and expected to return
295 a fully-rendered string.
296 ext (None or str): Optionally specify an extension to enforce on the
297 format. This enforcement occurs after obj is finalized to a string. If
298 the string doesn't end with ext, it will be appended.
299 """
300 def inner(var_assignments):
301 ret = ''
302 if isinstance(obj, basestring):
303 ret = obj % var_assignments
304 else:
305 ret = obj(var_assignments)
306 if ext and not ret.endswith(ext):
307 ret += ext
308 return ret
309 return inner
310
311
312 def config_item_context(CONFIG_SCHEMA, VAR_TEST_MAP, TEST_NAME_FORMAT):
313 """Create a configuration context.
314
315 Args:
316 CONFIG_SCHEMA: This is a function which can take a minimum of zero arguments
317 and returns an instance of BaseConfig. This BaseConfig
318 defines the schema for all configuration objects manipulated
319 in this context.
320 VAR_TEST_MAP: A dict mapping arg_name to an iterable of values. This
321 provides the test harness with sufficient information to
322 generate all possible permutations of inputs for the
323 CONFIG_SCHEMA function.
324 TEST_NAME_FORMAT: A string format (or function) for naming tests and test
325 expectation files. It will be formatted/called with a
326 dictionary of arg_name to value (using arg_names and
327 values generated from VAR_TEST_MAP).
328
329 Returns a config_ctx decorator for this context.
330 """
331 return ConfigContext(CONFIG_SCHEMA, VAR_TEST_MAP, TEST_NAME_FORMAT)
332
333
334 class AutoHide(object):
335 pass
336 AutoHide = AutoHide()
337
338
339 class ConfigBase(object):
340 """This is the root interface for all config schema types."""
341
342 def __init__(self, hidden=AutoHide):
343 """
344 Args:
345 hidden -
346 True: This object will be excluded from printing when the config blob
347 is rendered with ConfigGroup.as_jsonish(). You still have full
348 read/write access to this blob otherwise though.
349 False: This will be printed as part of ConfigGroup.as_jsonish()
350 AutoHide: This will be printed as part of ConfigGroup.as_jsonish() only
351 if self._is_default() is false.
352 """
353 # work around subclasses which override __setattr__
354 object.__setattr__(self, '_hidden_mode', hidden)
355 object.__setattr__(self, '_inclusions', set())
356
357 def get_val(self):
358 """Gets the native value of this config object."""
359 return self
360
361 def set_val(self, val):
362 """Resets the value of this config object using data in val."""
363 raise NotImplementedError
364
365 def reset(self):
366 """Resets the value of this config object to it's initial state."""
367 raise NotImplementedError
368
369 def as_jsonish(self, include_hidden=False):
370 """Returns the value of this config object as simple types."""
371 raise NotImplementedError
372
373 def complete(self):
374 """Returns True iff this configuraton blob is fully viable."""
375 raise NotImplementedError
376
377 def _is_default(self):
378 """Returns True iff this configuraton blob is the default value."""
379 raise NotImplementedError
380
381 @property
382 def _hidden(self):
383 """Returns True iff this configuraton blob is hidden."""
384 if self._hidden_mode is AutoHide:
385 return self._is_default()
386 return self._hidden_mode
387
388
389 class ConfigGroup(ConfigBase):
390 """Allows you to provide hierarchy to a configuration schema.
391
392 Example usage:
393 config_blob = ConfigGroup(
394 some_item = SimpleConfig(str),
395 group = ConfigGroup(
396 numbahs = SetConfig(int),
397 ),
398 )
399 config_blob.some_item = "hello"
400 config_blob.group.numbahs.update(range(10))
401 """
402
403 def __init__(self, hidden=AutoHide, **type_map):
404 """Expects type_map to be {python_name -> ConfigBase} instance."""
405 super(ConfigGroup, self).__init__(hidden)
406 assert type_map, 'A ConfigGroup with no type_map is meaningless.'
407
408 object.__setattr__(self, '_type_map', type_map)
409 for name, typeval in self._type_map.iteritems():
410 assert isinstance(typeval, ConfigBase)
411 object.__setattr__(self, name, typeval)
412
413 def __getattribute__(self, name):
414 obj = object.__getattribute__(self, name)
415 if isinstance(obj, ConfigBase):
416 return obj.get_val()
417 else:
418 return obj
419
420 def __setattr__(self, name, val):
421 obj = object.__getattribute__(self, name)
422 assert isinstance(obj, ConfigBase)
423 obj.set_val(val)
424
425 def __delattr__(self, name):
426 obj = object.__getattribute__(self, name)
427 assert isinstance(obj, ConfigBase)
428 obj.reset()
429
430 def set_val(self, val):
431 if isinstance(val, ConfigBase):
432 val = val.as_jsonish(include_hidden=True)
433 if isinstance(val, infra_types.FrozenDict):
434 val = infra_types.thaw(val)
435 assert isinstance(val, dict)
436 val = dict(val) # because we pop later.
437 for name, config_obj in self._type_map.iteritems():
438 if name in val:
439 try:
440 config_obj.set_val(val.pop(name))
441 except Exception as e:
442 raise Exception('While assigning key %r: %s' % (name, e))
443 assert not val, "Got extra keys while setting ConfigGroup: %s" % val
444
445 def as_jsonish(self, include_hidden=False):
446 return dict(
447 (n, v.as_jsonish(include_hidden)) for n, v in self._type_map.iteritems()
448 if include_hidden or not v._hidden) # pylint: disable=W0212
449
450 def reset(self):
451 for v in self._type_map.values():
452 v.reset()
453
454 def complete(self):
455 return all(v.complete() for v in self._type_map.values())
456
457 def _is_default(self):
458 # pylint: disable=W0212
459 return all(v._is_default() for v in self._type_map.values())
460
461
462 class ConfigList(ConfigBase, collections.MutableSequence):
463 """Allows you to provide an ordered repetition to a configuration schema.
464
465 Example usage:
466 config_blob = ConfigGroup(
467 some_items = ConfigList(
468 lambda: ConfigGroup(
469 herp = SimpleConfig(int),
470 derp = SimpleConfig(str)
471 )
472 )
473 )
474 config_blob.some_items.append({'herp': 1})
475 config_blob.some_items[0].derp = 'bob'
476 """
477
478 def __init__(self, item_schema, hidden=AutoHide):
479 """
480 Args:
481 item_schema: The schema of each object. Should be a function which returns
482 an instance of ConfigGroup.
483 """
484 super(ConfigList, self).__init__(hidden=hidden)
485 assert isinstance(item_schema, types.FunctionType)
486 assert isinstance(item_schema(), ConfigGroup)
487 self.item_schema = item_schema
488 self.data = []
489
490 def __getitem__(self, index):
491 return self.data.__getitem__(index)
492
493 def __setitem__(self, index, value):
494 datum = self.item_schema()
495 datum.set_val(value)
496 return self.data.__setitem__(index, datum)
497
498 def __delitem__(self, index):
499 return self.data.__delitem__(index)
500
501 def __len__(self):
502 return len(self.data)
503
504 def insert(self, index, value):
505 datum = self.item_schema()
506 datum.set_val(value)
507 return self.data.insert(index, datum)
508
509 def add(self):
510 self.append({})
511 return self[-1]
512
513 def reset(self):
514 self.data = []
515
516 def complete(self):
517 return all(i.complete() for i in self.data)
518
519 def set_val(self, data):
520 if isinstance(data, ConfigList):
521 data = data.as_jsonish(include_hidden=True)
522 assert isinstance(data, list)
523 self.reset()
524 for item in data:
525 self.append(item)
526
527 def as_jsonish(self, include_hidden=False):
528 return [i.as_jsonish(include_hidden) for i in self.data
529 if include_hidden or not i._hidden] # pylint: disable=W0212
530
531 def _is_default(self):
532 # pylint: disable=W0212
533 return all(v._is_default() for v in self.data)
534
535
536 class Dict(ConfigBase, collections.MutableMapping):
537 """Provides a semi-homogenous dict()-like configuration object."""
538
539 def __init__(self, item_fn=lambda i: i, jsonish_fn=dict, value_type=None,
540 hidden=AutoHide):
541 """
542 Args:
543 item_fn - A function which renders (k, v) pairs to input items for
544 jsonish_fn. Defaults to the identity function.
545 jsonish_fn - A function which renders a list of outputs of item_fn to a
546 JSON-compatiple python datatype. Defaults to dict().
547 value_type - A type object used for constraining/validating the values
548 assigned to this dictionary.
549 hidden - See ConfigBase.
550 """
551 super(Dict, self).__init__(hidden)
552 self.value_type = value_type
553 self.item_fn = item_fn
554 self.jsonish_fn = jsonish_fn
555 self.data = {}
556
557 def __getitem__(self, k):
558 return self.data.__getitem__(k)
559
560 def __setitem__(self, k, v):
561 if self.value_type:
562 assert isinstance(v, self.value_type)
563 return self.data.__setitem__(k, v)
564
565 def __delitem__(self, k):
566 return self.data.__delitem__(k)
567
568 def __iter__(self):
569 return iter(self.data)
570
571 def __len__(self):
572 return len(self.data)
573
574 def set_val(self, val):
575 if isinstance(val, Dict):
576 val = val.data
577 if isinstance(val, infra_types.FrozenDict):
578 val = dict(val)
579 assert isinstance(val, dict)
580 for v in val.itervalues():
581 assert isinstance(v, self.value_type), (
582 'Expected %r to be of type %r' % (v, self.value_type))
583 self.data = val
584
585 def as_jsonish(self, _include_hidden=None):
586 return self.jsonish_fn(map(
587 self.item_fn, sorted(self.data.iteritems(), key=lambda x: x[0])))
588
589 def reset(self):
590 self.data.clear()
591
592 def complete(self):
593 return True
594
595 def _is_default(self):
596 return not self.data
597
598
599 class List(ConfigBase, collections.MutableSequence):
600 """Provides a semi-homogenous list()-like configuration object."""
601
602 def __init__(self, inner_type, jsonish_fn=list, hidden=AutoHide):
603 """
604 Args:
605 inner_type - The type of data contained in this set, e.g. str, int, ...
606 Can also be a tuple of types to allow more than one type.
607 jsonish_fn - A function used to reduce the list() to a JSON-compatible
608 python datatype. Defaults to list().
609 hidden - See ConfigBase.
610 """
611 super(List, self).__init__(hidden)
612 self.inner_type = inner_type
613 self.jsonish_fn = jsonish_fn
614 self.data = []
615
616 def __getitem__(self, index):
617 return self.data[index]
618
619 def __setitem__(self, index, value):
620 assert isinstance(value, self.inner_type)
621 self.data[index] = value
622
623 def __delitem__(self, index):
624 del self.data
625
626 def __len__(self):
627 return len(self.data)
628
629 def __radd__(self, other):
630 if not isinstance(other, list):
631 other = list(other)
632 return other + self.data
633
634 def insert(self, index, value):
635 assert isinstance(value, self.inner_type)
636 self.data.insert(index, value)
637
638 def set_val(self, val):
639 for v in val:
640 assert isinstance(v, self.inner_type), (
641 'Expected %r to be of type %r' % (v, self.inner_type))
642 self.data = list(val)
643
644 def as_jsonish(self, _include_hidden=None):
645 return self.jsonish_fn(self.data)
646
647 def reset(self):
648 self.data = []
649
650 def complete(self):
651 return True
652
653 def _is_default(self):
654 return not self.data
655
656
657 class Set(ConfigBase, collections.MutableSet):
658 """Provides a semi-homogenous set()-like configuration object."""
659
660 def __init__(self, inner_type, jsonish_fn=list, hidden=AutoHide):
661 """
662 Args:
663 inner_type - The type of data contained in this set, e.g. str, int, ...
664 Can also be a tuple of types to allow more than one type.
665 jsonish_fn - A function used to reduce the set() to a JSON-compatible
666 python datatype. Defaults to list().
667 hidden - See ConfigBase.
668 """
669 super(Set, self).__init__(hidden)
670 self.inner_type = inner_type
671 self.jsonish_fn = jsonish_fn
672 self.data = set()
673
674 def __contains__(self, val):
675 return val in self.data
676
677 def __iter__(self):
678 return iter(self.data)
679
680 def __len__(self):
681 return len(self.data)
682
683 def add(self, value):
684 assert isinstance(value, self.inner_type)
685 self.data.add(value)
686
687 def update(self, values):
688 for value in values:
689 if value not in self:
690 self.add(value)
691
692 def discard(self, value):
693 self.data.discard(value)
694
695 def set_val(self, val):
696 for v in val:
697 assert isinstance(v, self.inner_type), (
698 'Expected %r to be of type %r' % (v, self.inner_type))
699 self.data = set(val)
700
701 def as_jsonish(self, _include_hidden=None):
702 return self.jsonish_fn(sorted(self.data))
703
704 def reset(self):
705 self.data = set()
706
707 def complete(self):
708 return True
709
710 def _is_default(self):
711 return not self.data
712
713
714 class Single(ConfigBase):
715 """Provides a configuration object which holds a single 'simple' type."""
716
717 def __init__(self, inner_type, jsonish_fn=lambda x: x, empty_val=None,
718 required=True, hidden=AutoHide):
719 """
720 Args:
721 inner_type - The type of data contained in this object, e.g. str, int, ...
722 Can also be a tuple of types to allow more than one type.
723 jsonish_fn - A function used to reduce the data to a JSON-compatible
724 python datatype. Default is the identity function.
725 emtpy_val - The value to use when initializing this object or when calling
726 reset().
727 required(bool) - True iff this config item is required to have a
728 non-empty_val in order for it to be considered complete().
729 hidden - See ConfigBase.
730 """
731 super(Single, self).__init__(hidden)
732 self.inner_type = inner_type
733 self.jsonish_fn = jsonish_fn
734 self.empty_val = empty_val
735 self.data = empty_val
736 self.required = required
737
738 def get_val(self):
739 return self.data
740
741 def set_val(self, val):
742 if isinstance(val, Single):
743 val = val.data
744 if val is not self.empty_val:
745 assert isinstance(val, self.inner_type), (
746 'Expected %r to be of type %r' % (val, self.inner_type))
747 self.data = val
748
749 def as_jsonish(self, _include_hidden=None):
750 return self.jsonish_fn(self.data)
751
752 def reset(self):
753 self.data = self.empty_val
754
755 def complete(self):
756 return not self.required or self.data is not self.empty_val
757
758 def _is_default(self):
759 return self.data is self.empty_val
760
761
762 class Static(ConfigBase):
763 """Holds a single, hidden, immutible data object.
764
765 This is very useful for holding the 'input' configuration values (i.e. those
766 which are in your VAR_TEST_MAP).
767 """
768
769 def __init__(self, value, hidden=AutoHide):
770 super(Static, self).__init__(hidden=hidden)
771 # Attempt to hash the value, which will ensure that it's immutable all the
772 # way down :).
773 hash(value)
774 self.data = value
775
776 def get_val(self):
777 return self.data
778
779 def set_val(self, val):
780 assert False, "Cannot assign to a Static config member"
781
782 def as_jsonish(self, _include_hidden=None):
783 return self.data
784
785 def reset(self):
786 assert False
787
788 def complete(self):
789 return True
790
791 def _is_default(self):
792 return True
OLDNEW
« no previous file with comments | « scripts/slave/recipe_api.py ('k') | scripts/slave/recipe_config_types.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698