29 from .comparison
import getComparisonName, compareScalars, compareConfigs
31 __all__ = (
"Config",
"Field",
"FieldValidationError")
35 Utility function for generating nested configuration names
37 if not prefix
and not name:
38 raise ValueError(
"Invalid name: cannot be None")
42 name = prefix +
"." + name
45 return "%s[%r]"%(name, index)
51 If appropriate perform type casting of value x to type dtype,
52 otherwise return the original value x
54 if dtype==float
and type(x)==int:
56 if dtype==int
and type(x)==long:
62 Utility function to generate a fully qualified type name.
64 This is used primarily in writing config files to be
65 executed later upon 'load'.
67 if hasattr(x,
'__module__')
and hasattr(x,
'__name__'):
71 if xtype.__module__ ==
'__builtin__':
74 return "%s.%s"%(xtype.__module__, xtype.__name__)
77 """A metaclass for Config
79 Adds a dictionary containing all Field class attributes
80 as a class attribute called '_fields', and adds the name of each field as
81 an instance variable of the field itself (so you don't have to pass the
82 name of the field to the field constructor).
85 type.__init__(self, name, bases, dict_)
87 self.
_source = traceback.extract_stack(limit=2)[0]
88 def getFields(classtype):
90 bases=list(classtype.__bases__)
93 fields.update(getFields(b))
95 for k, v
in classtype.__dict__.iteritems():
96 if isinstance(v, Field):
100 fields = getFields(self)
101 for k, v
in fields.iteritems():
102 setattr(self, k, copy.deepcopy(v))
105 if isinstance(value, Field):
108 type.__setattr__(self, name, value)
112 Custom exception class which holds additional information useful to
113 debuggin Config errors:
114 - fieldType: type of the Field which incurred the error
115 - fieldName: name of the Field which incurred the error
116 - fullname: fully qualified name of the Field instance
117 - history: full history of all changes to the Field instance
118 - fieldSource: file and line number of the Field definition
124 self.
history = config.history.setdefault(field.name, [])
127 error=
"%s '%s' failed validation: %s\n"\
128 "For more information read the Field definition at:\n%s"\
129 "And the Config definition at:\n%s"%\
130 (self.fieldType.__name__, self.
fullname, msg,
133 ValueError.__init__(self, error)
137 """A field in a a Config.
139 Instances of Field should be class attributes of Config subclasses:
140 Field only supports basic data types (int, float, complex, bool, str)
142 class Example(Config):
143 myInt = Field(int, "an integer field!", default=0)
145 supportedTypes=(str, bool, float, int, complex)
147 def __init__(self, doc, dtype, default=None, check=None, optional=False):
148 """Initialize a Field.
150 dtype ------ Data type for the field.
151 doc -------- Documentation for the field.
152 default ---- A default value for the field.
153 check ------ A callable to be called with the field value that returns
154 False if the value is invalid. More complex inter-field
155 validation can be written as part of Config validate()
156 method; this will be ignored if set to None.
157 optional --- When False, Config validate() will fail if value is None
160 raise ValueError(
"Unsupported Field dtype %s"%
_typeStr(dtype))
161 source = traceback.extract_stack(limit=2)[0]
162 self.
_setup(doc=doc, dtype=dtype, default=default, check=check, optional=optional, source=source)
165 def _setup(self, doc, dtype, default, check, optional, source):
167 Convenience function provided to simplify initialization of derived
180 Rename an instance of this field, not the field itself.
181 This is invoked by the owning config object and should not be called
184 Only useful for fields which hold sub-configs.
185 Fields which hold subconfigs should rename each sub-config with
186 the full field name as generated by _joinNamePath
192 Base validation for any field.
193 Ensures that non-optional fields are not None.
194 Ensures type correctness
195 Ensures that user-provided check function is valid
196 Most derived Field types should call Field.validate if they choose
197 to re-implement validate
199 value = self.__get__(instance)
200 if not self.optional
and value
is None:
201 raise FieldValidationError(self, instance,
"Required value cannot be None")
205 Make this field read-only.
206 Only important for fields which hold sub-configs.
207 Fields which hold subconfigs should freeze each sub-config.
213 Validate a value that is not None
215 This is called from __set__
216 This is not part of the Field API. However, simple derived field types
217 may benifit from implementing _validateValue
222 if not isinstance(value, self.dtype):
223 msg =
"Value %s is of incorrect type %s. Expected type %s"%\
226 if self.check
is not None and not self.check(value):
227 msg =
"Value %s is not a valid value"%str(value)
228 raise ValueError(msg)
230 def save(self, outfile, instance):
232 Saves an instance of this field to file.
233 This is invoked by the owning config object, and should not be called
236 outfile ---- an open output stream.
240 if isinstance(value, float)
and (math.isinf(value)
or math.isnan(value)):
242 print >> outfile,
"%s=float('%r')"%(fullname, value)
244 print >> outfile,
"%s=%r"%(fullname, value)
248 Convert the field value so that it can be set as the value of an item
250 This is invoked by the owning config object and should not be called
253 Simple values are passed through. Complex data structures must be
254 manipulated. For example, a field holding a sub-config should, instead
255 of the subconfig object, return a dict where the keys are the field
256 names in the subconfig, and the values are the field values in the
261 def __get__(self, instance, owner=None, at=None, label="default"):
263 Define how attribute access should occur on the Config instance
264 This is invoked by the owning config object and should not be called
267 When the field attribute is accessed on a Config class object, it
268 returns the field object itself in order to allow inspection of
271 When the field attribute is access on a config instance, the actual
272 value described by the field (and held by the Config instance) is
275 if instance
is None or not isinstance(instance, Config):
278 return instance._storage[self.name]
280 def __set__(self, instance, value, at=None, label='assignment'):
282 Describe how attribute setting should occur on the config instance.
283 This is invoked by the owning config object and should not be called
286 Derived Field classes may need to override the behavior. When overriding
287 __set__, Field authors should follow the following rules:
288 * Do not allow modification of frozen configs
289 * Validate the new value *BEFORE* modifying the field. Except if the
290 new value is None. None is special and no attempt should be made to
291 validate it until Config.validate is called.
292 * Do not modify the Config instance to contain invalid values.
293 * If the field is modified, update the history of the field to reflect the
296 In order to decrease the need to implement this method in derived Field
297 types, value validation is performed in the method _validateValue. If
298 only the validation step differs in the derived Field, it is simpler to
299 implement _validateValue than to re-implement __set__. More complicated
300 behavior, however, may require a reimplementation.
305 history = instance._history.setdefault(self.name, [])
306 if value
is not None:
310 except BaseException, e:
313 instance._storage[self.name] = value
315 at = traceback.extract_stack()[:-1]
316 history.append((value, at, label))
320 Describe how attribute deletion should occur on the Config instance.
321 This is invoked by the owning config object and should not be called
325 at = traceback.extract_stack()[:-1]
326 self.
__set__(instance,
None, at=at, label=label)
328 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
329 """Helper function for Config.compare; used to compare two fields for equality.
331 Must be overridden by more complex field types.
333 @param[in] instance1 LHS Config instance to compare.
334 @param[in] instance2 RHS Config instance to compare.
335 @param[in] shortcut If True, return as soon as an inequality is found.
336 @param[in] rtol Relative tolerance for floating point comparisons.
337 @param[in] atol Absolute tolerance for floating point comparisons.
338 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
339 to report inequalities.
341 Floating point comparisons are performed by numpy.allclose; refer to that for details.
343 v1 = getattr(instance1, self.name)
344 v2 = getattr(instance2, self.name)
352 """An Importer (for sys.meta_path) that records which modules are being imported.
354 Objects also act as Context Managers, so you can:
355 with RecordingImporter() as importer:
357 print "Imported: " + importer.getModules()
358 This ensures it is properly uninstalled when done.
360 This class makes no effort to do any importing itself.
363 """Create and install the Importer"""
369 sys.meta_path = [self] + sys.meta_path
377 """Uninstall the Importer"""
381 """Called as part of the 'import' chain of events.
383 We return None because we don't do any importing.
385 self._modules.add(fullname)
389 """Return the set of modules that were imported."""
393 """Base class for control objects.
395 A Config object will usually have several Field instances as class
396 attributes; these are used to define most of the base class behavior.
397 Simple derived class should be able to be defined simply by setting those
400 Config also emulates a dict of field name: field value
403 __metaclass__ = ConfigMeta
406 """!Iterate over fields
408 return self._fields.__iter__()
411 """!Return the list of field names
413 return self._storage.keys()
415 """!Return the list of field values
417 return self._storage.values()
419 """!Return the list of (field name, field value) pairs
421 return self._storage.items()
424 """!Iterate over (field name, field value) pairs
426 return self._storage.iteritems()
429 """!Iterate over field values
431 return self.storage.itervalues()
434 """!Iterate over field names
436 return self.storage.iterkeys()
439 """!Return True if the specified field exists in this config
441 @param[in] name field name to test for
443 return self._storage.__contains__(name)
446 """!Allocate a new Config object.
448 In order to ensure that all Config object are always in a proper
449 state when handed to users or to derived Config classes, some
450 attributes are handled at allocation time rather than at initialization
452 This ensures that even if a derived Config class implements __init__,
453 the author does not need to be concerned about when or even if he
454 should call the base Config.__init__
456 name = kw.pop(
"__name",
None)
457 at = kw.pop(
"__at", traceback.extract_stack()[:-1])
459 kw.pop(
"__label",
"default")
461 instance = object.__new__(cls)
462 instance._frozen=
False
464 instance._storage = {}
465 instance._history = {}
466 instance._imports = set()
468 for field
in instance._fields.itervalues():
469 instance._history[field.name]=[]
470 field.__set__(instance, field.default, at=at+[field.source], label=
"default")
472 instance.setDefaults()
474 instance.update(__at=at, **kw)
478 """Reduction for pickling (function with arguments to reproduce).
480 We need to condense and reconstitute the Config, since it may contain lambdas
481 (as the 'check' elements) that cannot be pickled.
483 stream = io.BytesIO()
485 return (unreduceConfig, (self.__class__, stream.getvalue()))
489 Derived config classes that must compute defaults rather than using the
490 Field defaults should do so here.
491 To correctly use inherited defaults, implementations of setDefaults()
492 must call their base class' setDefaults()
497 """!Update values specified by the keyword arguments
499 @warning The '__at' and '__label' keyword arguments are special internal
500 keywords. They are used to strip out any internal steps from the
501 history tracebacks of the config. Modifying these keywords allows users
502 to lie about a Config's history. Please do not do so!
504 at = kw.pop(
"__at", traceback.extract_stack()[:-1])
505 label = kw.pop(
"__label",
"update")
507 for name, value
in kw.iteritems():
509 field = self._fields[name]
510 field.__set__(self, value, at=at, label=label)
512 raise KeyError(
"No field of name %s exists in config type %s"%(name,
_typeStr(self)))
514 def load(self, filename, root="config"):
515 """!Modify this config in place by executing the Python code in the named file.
517 @param[in] filename name of file containing config override code
518 @param[in] root name of variable in file that refers to the config being overridden
520 For example: if the value of root is "config" and the file contains this text:
521 "config.myField = 5" then this config's field "myField" is set to 5.
523 @deprecated For purposes of backwards compatibility, older config files that use
524 root="root" instead of root="config" will be loaded with a warning printed to sys.stderr.
525 This feature will be removed at some point.
527 with open(filename)
as f:
528 code = compile(f.read(), filename=filename, mode=
"exec")
532 """!Modify this config in place by executing the python code in the provided stream.
534 @param[in] stream open file object, string or compiled string containing config override code
535 @param[in] root name of variable in stream that refers to the config being overridden
536 @param[in] filename name of config override file, or None if unknown or contained
537 in the stream; used for error reporting
539 For example: if the value of root is "config" and the stream contains this text:
540 "config.myField = 5" then this config's field "myField" is set to 5.
542 @deprecated For purposes of backwards compatibility, older config files that use
543 root="root" instead of root="config" will be loaded with a warning printed to sys.stderr.
544 This feature will be removed at some point.
549 exec stream
in {}, local
550 except NameError
as e:
551 if root ==
"config" and "root" in e.args[0]:
555 filename = getattr(stream,
"co_filename",
None)
557 filename = getattr(stream,
"name",
"?")
558 sys.stderr.write(
"Config override file %r" % (filename,) + \
559 " appears to use 'root' instead of 'config'; trying with 'root'")
560 local = {
"root": self}
561 exec stream
in {}, local
563 self._imports.update(importer.getModules())
565 def save(self, filename, root="config"):
566 """!Save a python script to the named file, which, when loaded, reproduces this Config
568 @param[in] filename name of file to which to write the config
569 @param[in] root name to use for the root config variable; the same value must be used when loading
571 with open(filename,
'w')
as outfile:
575 """!Save a python script to a stream, which, when loaded, reproduces this Config
577 @param outfile [inout] open file object to which to write the config
578 @param root [in] name to use for the root config variable; the same value must be used when loading
583 configType = type(self)
585 print >> outfile,
"import %s" % (configType.__module__)
586 print >> outfile,
"assert type(%s)==%s, 'config is of type %%s.%%s" % (root, typeString), \
587 "instead of %s' %% (type(%s).__module__, type(%s).__name__)" % (typeString, root, root)
593 """!Make this Config and all sub-configs read-only
596 for field
in self._fields.itervalues():
600 """!Save this Config to an open stream object
602 for imp
in self._imports:
603 if imp
in sys.modules
and sys.modules[imp]
is not None:
604 print >> outfile,
"import %s" % imp
605 for field
in self._fields.itervalues():
606 field.save(outfile, self)
609 """!Return a dict of field name: value
611 Correct behavior is dependent on proper implementation of Field.toDict. If implementing a new
612 Field type, you may need to implement your own toDict method.
615 for name, field
in self._fields.iteritems():
616 dict_[name] = field.toDict(self)
620 """!Rename this Config object in its parent config
622 @param[in] name new name for this config in its parent config
624 Correct behavior is dependent on proper implementation of Field.rename. If implementing a new
625 Field type, you may need to implement your own rename method.
628 for field
in self._fields.itervalues():
632 """!Validate the Config; raise an exception if invalid
634 The base class implementation performs type checks on all fields by
635 calling Field.validate().
637 Complex single-field validation can be defined by deriving new Field
638 types. As syntactic sugar, some derived Field types are defined in
639 this module which handle recursing into sub-configs
640 (ConfigField, ConfigChoiceField)
642 Inter-field relationships should only be checked in derived Config
643 classes after calling this method, and base validation is complete
645 for field
in self._fields.itervalues():
649 """!Format the specified config field's history to a more human-readable format
651 @param[in] name name of field whose history is wanted
652 @param[in] kwargs keyword arguments for lsst.pex.config.history.format
653 @return a string containing the formatted history
656 return pexHist.format(self, name, **kwargs)
659 Read-only history property
661 history = property(
lambda x: x._history)
664 """!Regulate which attributes can be set
666 Unlike normal python objects, Config objects are locked such
667 that no additional attributes nor properties may be added to them
670 Although this is not the standard Python behavior, it helps to
671 protect users from accidentally mispelling a field name, or
672 trying to set a non-existent field.
674 if attr
in self._fields:
676 at=traceback.extract_stack()[:-1]
678 self._fields[attr].__set__(self, value, at=at, label=label)
679 elif hasattr(getattr(self.__class__, attr,
None),
'__set__'):
681 return object.__setattr__(self, attr, value)
682 elif attr
in self.__dict__
or attr
in (
"_name",
"_history",
"_storage",
"_frozen",
"_imports"):
684 self.__dict__[attr] = value
687 raise AttributeError(
"%s has no attribute %s"%(
_typeStr(self), attr))
690 if attr
in self._fields:
692 at=traceback.extract_stack()[:-1]
693 self._fields[attr].__delete__(self, at=at, label=label)
695 object.__delattr__(self, attr)
698 if type(other) == type(self):
699 for name
in self._fields:
700 thisValue = getattr(self, name)
701 otherValue = getattr(other, name)
702 if isinstance(thisValue, float)
and math.isnan(thisValue):
703 if not math.isnan(otherValue):
705 elif thisValue != otherValue:
711 return not self.
__eq__(other)
719 ", ".join(
"%s=%r" % (k, v)
for k, v
in self.
toDict().
iteritems()
if v
is not None)
722 def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None):
723 """!Compare two Configs for equality; return True if equal
725 If the Configs contain RegistryFields or ConfigChoiceFields, unselected Configs
726 will not be compared.
728 @param[in] other Config object to compare with self.
729 @param[in] shortcut If True, return as soon as an inequality is found.
730 @param[in] rtol Relative tolerance for floating point comparisons.
731 @param[in] atol Absolute tolerance for floating point comparisons.
732 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
733 to report inequalities.
735 Floating point comparisons are performed by numpy.allclose; refer to that for details.
737 name1 = self.
_name if self.
_name is not None else "config"
738 name2 = other._name
if other._name
is not None else "config"
741 rtol=rtol, atol=atol, output=output)
745 config.loadFromStream(stream)
def keys
Return the list of field names.
def __setattr__
Regulate which attributes can be set.
def freeze
Make this Config and all sub-configs read-only.
def saveToStream
Save a python script to a stream, which, when loaded, reproduces this Config.
def loadFromStream
Modify this config in place by executing the python code in the provided stream.
def compare
Compare two Configs for equality; return True if equal.
def iteritems
Iterate over (field name, field value) pairs.
def __contains__
Return True if the specified field exists in this config.
def save
Save a python script to the named file, which, when loaded, reproduces this Config.
def _rename
Rename this Config object in its parent config.
def _save
Save this Config to an open stream object.
def load
Modify this config in place by executing the Python code in the named file.
def itervalues
Iterate over field values.
def iterkeys
Iterate over field names.
def toDict
Return a dict of field name: value.
def __iter__
Iterate over fields.
def formatHistory
Format the specified config field's history to a more human-readable format.
def items
Return the list of (field name, field value) pairs.
def update
Update values specified by the keyword arguments.
def validate
Validate the Config; raise an exception if invalid.
def __new__
Allocate a new Config object.
def values
Return the list of field values.