30 from .comparison
import *
32 __all__ = (
"Config",
"Field",
"FieldValidationError")
36 Utility function for generating nested configuration names
38 if not prefix
and not name:
39 raise ValueError(
"Invalid name: cannot be None")
43 name = prefix +
"." + name
46 return "%s[%r]"%(name, index)
52 If appropriate perform type casting of value x to type dtype,
53 otherwise return the original value x
55 if dtype==float
and type(x)==int:
57 if dtype==int
and type(x)==long:
63 Utility function to generate a fully qualified type name.
65 This is used primarily in writing config files to be
66 executed later upon 'load'.
68 if hasattr(x,
'__module__')
and hasattr(x,
'__name__'):
72 if xtype.__module__ ==
'__builtin__':
75 return "%s.%s"%(xtype.__module__, xtype.__name__)
78 """A metaclass for Config
80 Adds a dictionary containing all Field class attributes
81 as a class attribute called '_fields', and adds the name of each field as
82 an instance variable of the field itself (so you don't have to pass the
83 name of the field to the field constructor).
86 type.__init__(self, name, bases, dict_)
88 self.
_source = traceback.extract_stack(limit=2)[0]
89 def getFields(classtype):
91 bases=list(classtype.__bases__)
94 fields.update(getFields(b))
96 for k, v
in classtype.__dict__.iteritems():
97 if isinstance(v, Field):
101 fields = getFields(self)
102 for k, v
in fields.iteritems():
103 setattr(self, k, copy.deepcopy(v))
106 if isinstance(value, Field):
109 type.__setattr__(self, name, value)
113 Custom exception class which holds additional information useful to
114 debuggin Config errors:
115 - fieldType: type of the Field which incurred the error
116 - fieldName: name of the Field which incurred the error
117 - fullname: fully qualified name of the Field instance
118 - history: full history of all changes to the Field instance
119 - fieldSource: file and line number of the Field definition
125 self.
history = config.history.setdefault(field.name, [])
128 error=
"%s '%s' failed validation: %s\n"\
129 "For more information read the Field definition at:\n%s"\
130 "And the Config definition at:\n%s"%\
131 (self.fieldType.__name__, self.
fullname, msg,
134 ValueError.__init__(self, error)
138 """A field in a a Config.
140 Instances of Field should be class attributes of Config subclasses:
141 Field only supports basic data types (int, float, complex, bool, str)
143 class Example(Config):
144 myInt = Field(int, "an integer field!", default=0)
146 supportedTypes=(str, bool, float, int, complex)
148 def __init__(self, doc, dtype, default=None, check=None, optional=False):
149 """Initialize a Field.
151 dtype ------ Data type for the field.
152 doc -------- Documentation for the field.
153 default ---- A default value for the field.
154 check ------ A callable to be called with the field value that returns
155 False if the value is invalid. More complex inter-field
156 validation can be written as part of Config validate()
157 method; this will be ignored if set to None.
158 optional --- When False, Config validate() will fail if value is None
161 raise ValueError(
"Unsupported Field dtype %s"%
_typeStr(dtype))
162 source = traceback.extract_stack(limit=2)[0]
163 self.
_setup(doc=doc, dtype=dtype, default=default, check=check, optional=optional, source=source)
166 def _setup(self, doc, dtype, default, check, optional, source):
168 Convenience function provided to simplify initialization of derived
181 Rename an instance of this field, not the field itself.
182 This is invoked by the owning config object and should not be called
185 Only useful for fields which hold sub-configs.
186 Fields which hold subconfigs should rename each sub-config with
187 the full field name as generated by _joinNamePath
193 Base validation for any field.
194 Ensures that non-optional fields are not None.
195 Ensures type correctness
196 Ensures that user-provided check function is valid
197 Most derived Field types should call Field.validate if they choose
198 to re-implement validate
200 value = self.__get__(instance)
201 if not self.optional
and value
is None:
202 raise FieldValidationError(self, instance,
"Required value cannot be None")
206 Make this field read-only.
207 Only important for fields which hold sub-configs.
208 Fields which hold subconfigs should freeze each sub-config.
214 Validate a value that is not None
216 This is called from __set__
217 This is not part of the Field API. However, simple derived field types
218 may benifit from implementing _validateValue
223 if not isinstance(value, self.dtype):
224 msg =
"Value %s is of incorrect type %s. Expected type %s"%\
227 if self.check
is not None and not self.check(value):
228 msg =
"Value %s is not a valid value"%str(value)
229 raise ValueError(msg)
231 def save(self, outfile, instance):
233 Saves an instance of this field to file.
234 This is invoked by the owning config object, and should not be called
237 outfile ---- an open output stream.
241 if isinstance(value, float)
and (math.isinf(value)
or math.isnan(value)):
243 print >> outfile,
"%s=float('%r')"%(fullname, value)
245 print >> outfile,
"%s=%r"%(fullname, value)
249 Convert the field value so that it can be set as the value of an item
251 This is invoked by the owning config object and should not be called
254 Simple values are passed through. Complex data structures must be
255 manipulated. For example, a field holding a sub-config should, instead
256 of the subconfig object, return a dict where the keys are the field
257 names in the subconfig, and the values are the field values in the
262 def __get__(self, instance, owner=None, at=None, label="default"):
264 Define how attribute access should occur on the Config instance
265 This is invoked by the owning config object and should not be called
268 When the field attribute is accessed on a Config class object, it
269 returns the field object itself in order to allow inspection of
272 When the field attribute is access on a config instance, the actual
273 value described by the field (and held by the Config instance) is
276 if instance
is None or not isinstance(instance, Config):
279 return instance._storage[self.name]
281 def __set__(self, instance, value, at=None, label='assignment'):
283 Describe how attribute setting should occur on the config instance.
284 This is invoked by the owning config object and should not be called
287 Derived Field classes may need to override the behavior. When overriding
288 __set__, Field authors should follow the following rules:
289 * Do not allow modification of frozen configs
290 * Validate the new value *BEFORE* modifying the field. Except if the
291 new value is None. None is special and no attempt should be made to
292 validate it until Config.validate is called.
293 * Do not modify the Config instance to contain invalid values.
294 * If the field is modified, update the history of the field to reflect the
297 In order to decrease the need to implement this method in derived Field
298 types, value validation is performed in the method _validateValue. If
299 only the validation step differs in the derived Field, it is simpler to
300 implement _validateValue than to re-implement __set__. More complicated
301 behavior, however, may require a reimplementation.
306 history = instance._history.setdefault(self.name, [])
307 if value
is not None:
311 except BaseException, e:
314 instance._storage[self.name] = value
316 at = traceback.extract_stack()[:-1]
317 history.append((value, at, label))
321 Describe how attribute deletion should occur on the Config instance.
322 This is invoked by the owning config object and should not be called
326 at = traceback.extract_stack()[:-1]
327 self.
__set__(instance,
None, at=at, label=label)
329 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
330 """Helper function for Config.compare; used to compare two fields for equality.
332 Must be overridden by more complex field types.
334 @param[in] instance1 LHS Config instance to compare.
335 @param[in] instance2 RHS Config instance to compare.
336 @param[in] shortcut If True, return as soon as an inequality is found.
337 @param[in] rtol Relative tolerance for floating point comparisons.
338 @param[in] atol Absolute tolerance for floating point comparisons.
339 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
340 to report inequalities.
342 Floating point comparisons are performed by numpy.allclose; refer to that for details.
344 v1 = getattr(instance1, self.name)
345 v2 = getattr(instance2, self.name)
353 """An Importer (for sys.meta_path) that records which modules are being imported.
355 Objects also act as Context Managers, so you can:
356 with RecordingImporter() as importer:
358 print "Imported: " + importer.getModules()
359 This ensures it is properly uninstalled when done.
361 This class makes no effort to do any importing itself.
364 """Create and install the Importer"""
370 sys.meta_path = [self] + sys.meta_path
378 """Uninstall the Importer"""
382 """Called as part of the 'import' chain of events.
384 We return None because we don't do any importing.
386 self._modules.add(fullname)
390 """Return the set of modules that were imported."""
394 """Base class for control objects.
396 A Config object will usually have several Field instances as class
397 attributes; these are used to define most of the base class behavior.
398 Simple derived class should be able to be defined simply by setting those
402 __metaclass__ = ConfigMeta
406 Enable iterating over a config allows inspection of a Config's fields,
407 so that a Config behaves like a dict mapping field names to field
410 return self._fields.__iter__()
414 Return the list of field names
416 return self._storage.keys()
419 Return the list of field values
421 return self._storage.values()
424 Return the list of (field name, field value) pairs
426 return self._storage.items()
430 Enable iterate over (field name, field value) pairs
432 return self._storage.iteritems()
435 Enable iteration over field values
437 return self.storage.itervalues()
440 Enable iteration over field names
442 return self.storage.iterkeys()
446 Determines whether the field 'name' exists in this config
448 return self._storage.__contains__(name)
452 Allocate a new Config object.
454 In order to ensure that all Config object are always in a proper
455 state when handed to users or to derived Config classes, some
456 attributes are handled at allocation time rather than at initialization
458 This ensures that even if a derived Config class implements __init__,
459 the author does not need to be concerned about when or even if he
460 should call the base Config.__init__
462 name=kw.pop(
"__name",
None)
463 at=kw.pop(
"__at", traceback.extract_stack()[:-1])
464 label=kw.pop(
"__label",
"default")
466 instance = object.__new__(cls)
467 instance._frozen=
False
469 instance._storage = {}
470 instance._history = {}
471 instance._imports = set()
473 for field
in instance._fields.itervalues():
474 instance._history[field.name]=[]
475 field.__set__(instance, field.default, at=at+[field.source], label=
"default")
477 instance.setDefaults()
479 instance.update(__at=at, **kw)
483 """Reduction for pickling (function with arguments to reproduce).
485 We need to condense and reconstitute the Config, since it may contain lambdas
486 (as the 'check' elements) that cannot be pickled.
488 stream = io.BytesIO()
490 return (unreduceConfig, (self.__class__, stream.getvalue()))
494 Derived config classes that must compute defaults rather than using the
495 Field defaults should do so here.
496 To correctly use inherited defaults, implementations of setDefaults()
497 must call their base class' setDefaults()
503 Treat the Config as a dict, updating values as provided by the keyword
506 The '__at' and '__label' keyword arguments are special internal
507 keywords. They are used to strip out any internal steps from the
508 history tracebacks of the config. Modifying these keywords allows users
509 to lie about a Config's history. Please do not do so!
511 at=kw.pop(
"__at", traceback.extract_stack()[:-1])
512 label = kw.pop(
"__label",
"update")
514 for name, value
in kw.iteritems():
516 field = self._fields[name]
517 field.__set__(self, value, at=at, label=label)
519 raise KeyError(
"No field of name %s exists in config type %s"%(name,
_typeStr(self)))
521 def load(self, filename, root="root"):
523 Load override from files, modify this config in place by executing the
524 Python code in the given file.
526 The file should modify a Config named root
533 execfile(filename, {}, local)
534 self._imports.update(importer.getModules())
538 Modify this config in place by executign the python code in the
541 The stream should modify a Config named 'root', e.g.: root.myField = 5
545 exec stream
in {}, local
546 self._imports.update(importer.getModules())
548 def save(self, filename, root="root"):
550 Generates a python script at the given filename, which, when loaded,
551 reproduces this Config.
553 @param filename [in] name of file to write to
554 @param root [in] name to use for the root config variable
555 If not "root", must match what is used in load())
557 with open(filename,
'w')
as outfile:
562 Generates a python script to the given open file object, which, when
563 loaded, reproduces this Config.
565 @param outfile [inout] open file object to write to
566 @param root [in] name to use for the root config variable
567 If not "root", must match what is used in load())
572 configType = type(self)
574 print >> outfile,
"import %s" % (configType.__module__)
575 print >> outfile,
"assert type(%s)==%s, 'config is of type %%s.%%s" % (root, typeString), \
576 "instead of %s' %% (type(root).__module__, type(root).__name__)" % (typeString,)
583 Make this Config, and recursively all sub-configs read-only
586 for field
in self._fields.itervalues():
591 Internal use only. Save this Config to an open stream object
593 for imp
in self._imports:
594 if imp
in sys.modules
and sys.modules[imp]
is not None:
595 print >> outfile,
"import %s" % imp
596 for field
in self._fields.itervalues():
597 field.save(outfile, self)
601 Convert this Config into a dict whose keys are field names,
602 and whose values are field values.
604 Correct behavior is dependent on proper implementation of
605 Field.toDict. If implementing a new Field type, you may need to
606 implement your own toDict method.
609 for name, field
in self._fields.iteritems():
610 dict_[name] = field.toDict(self)
616 Rename this Config object to reflect its position in a Config hierarchy
619 Correct behavior is dependent on proper implementation of
620 Field.rename. If implementing a new Field type, you may need to
621 implement your own rename method.
624 for field
in self._fields.itervalues():
631 The base class implementation performs type checks on all fields by
632 calling Field.validate().
634 Complex single-field validation can be defined by deriving new Field
635 types. As syntactic sugar, some derived Field types are defined in
636 this module which handle recursing into sub-configs
637 (ConfigField, ConfigChoiceField)
639 Inter-field relationships should only be checked in derived Config
640 classes after calling this method, and base validation is complete
642 for field
in self._fields.itervalues():
647 Format the config's history to a more human-readable format
650 return pexHist.format(self, name, **kwargs)
653 Read-only history property
655 history = property(
lambda x: x._history)
659 Regulate which attributes can be set.
661 Unlike normal python objects, Config objects are locked such
662 that no additional attributes nor properties may be added to them
665 Although this is not the standard Python behavior, it helps to
666 protect users from accidentally mispelling a field name, or
667 trying to set a non-existent field.
669 if attr
in self._fields:
671 at=traceback.extract_stack()[:-1]
673 self._fields[attr].__set__(self, value, at=at, label=label)
674 elif hasattr(getattr(self.__class__, attr,
None),
'__set__'):
676 return object.__setattr__(self, attr, value)
677 elif attr
in self.__dict__
or attr
in (
"_name",
"_history",
"_storage",
"_frozen",
"_imports"):
679 self.__dict__[attr] = value
682 raise AttributeError(
"%s has no attribute %s"%(
_typeStr(self), attr))
685 if attr
in self._fields:
687 at=traceback.extract_stack()[:-1]
688 self._fields[attr].__delete__(self, at=at, label=label)
690 object.__delattr__(self, attr)
693 if type(other) == type(self):
694 for name
in self._fields:
695 thisValue = getattr(self, name)
696 otherValue = getattr(other, name)
697 if isinstance(thisValue, float)
and math.isnan(thisValue):
698 if not math.isnan(otherValue):
700 elif thisValue != otherValue:
706 return not self.
__eq__(other)
714 ", ".join(
"%s=%r" % (k, v)
for k, v
in self.
toDict().
iteritems()
if v
is not None)
717 def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None):
718 """Compare two Configs for equality.
720 If the Configs contain RegistryFields or ConfigChoiceFields, unselected Configs
721 will not be compared.
723 @param[in] other Config object to compare with self.
724 @param[in] shortcut If True, return as soon as an inequality is found.
725 @param[in] rtol Relative tolerance for floating point comparisons.
726 @param[in] atol Absolute tolerance for floating point comparisons.
727 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
728 to report inequalities.
730 Floating point comparisons are performed by numpy.allclose; refer to that for details.
732 name1 = self.
_name if self.
_name is not None else "root"
733 name2 = other._name
if other._name
is not None else "root"
736 rtol=rtol, atol=atol, output=output)
740 config.loadFromStream(stream)