""" open/dulcinea/lib/property/property.py """ from dulcinea.base import DulcineaPersistent from dulcinea.property.property_template import PropertyTemplate from dulcinea.tolerance import Tolerance from qp.lib.spec import anything, spec, specify, add_getters_and_setters from qpy import xml, stringify class Property (DulcineaPersistent): template_is = spec( PropertyTemplate, "defines attributes of this Property such as name, type and " "the constraints on the value") value_is = spec( anything, "the value this Property has (may be None which means no value)") tolerance_is = spec( (None, Tolerance), "the relative tolerance of this Property (in percent). May be " "None.") def __init__(self, template, value=None): specify(self, template=template, tolerance=None) self.set_value(value) def __str__(self): if self.value is None: return "%s (no value)" % self.get_name() elif not self.value: # empty list/string/etc. return "%s (empty value)" % self.get_name() else: return "%s = %s" % (self.get_name(), self.format()) def __eq__(self, other): if not isinstance(other, Property): return False return (self.value == other.value and self.tolerance == other.tolerance and self.get_master() is other.get_master()) def __ne__(self, other): return not (self == other) def is_required(self): return self.template.is_required() def is_hidden(self): return self.template.is_hidden() def is_input(self): return 0 def is_property(self): return 1 def set_value(self, value): """(any) May raise TypeError. """ self.template.check_type(value) self.value = value def allow_tolerance(self): return self.template.get_type().allow_tolerance() def check_value(self): self.template.check_value(self.value) def format(self, html=0, show_tolerance=0): """Return the Property's value (and unit, if any) as a useful, standalone string. Return None if the value is undefined.""" if self.value is None: return None property_type = self.template.get_type() s = property_type.format_value(self.value, html=html) if show_tolerance and self.tolerance is not None: s += ' ' + xml(self.tolerance.format(html=html)) return s def check_type(self): """() Ensure that 'self.value' is compatible with the Property type. Raise PropertyTypeError if not. """ self.get_type().check_value(self.value) def simplify(self): """() -> (string|int|float|None) Return a simplified version of this Property's value. Physical values are converted to use the default unit from the master template and are returned as floats. Materials are returned as strings (using their names). If self is an InputTemplate, simplify the default value """ return self.get_type().simplify_value(self.value) # -- Delegates to our template ------------------------------------- def is_discrete(self): return self.template.is_discrete() def is_atomic(self): return self.template.is_atomic() def is_list(self): return self.template.is_list() def is_table(self): return self.template.is_table() def is_array(self): return self.template.is_array() def is_aggregate(self): return self.template.is_aggregate() def is_time(self): return self.template.is_time() def get_master(self): return self.template.get_master() def get_name(self): return self.template.get_name() def get_type(self): return self.template.get_type() def get_title(self): return self.template.get_title() def get_description(self): return self.template.get_description() def get_hint(self, html=0): if html: texttype = xml else: texttype = stringify hint = texttype(self.get_description() or "") if not (self.is_discrete() or self.get_constraint() is None): if hint: hint += ', ' hint += self.template.explain_constraint(html=html) return hint def get_constraint(self): constraint = self.template.get_constraint() if self.get_type().is_material(): materials = set() for material in constraint: materials.update(material.get_leaves()) return list(materials) else: return constraint def get_material_constraint(self): assert self.get_type().is_material() materials = set() for material in self.template.get_constraint(): materials.add(material) materials.update(material.get_descendants()) return list(materials) def get_allowed_units(self): return self.template.get_allowed_units() add_getters_and_setters(Property) def get_example_properties(property_list): """() -> [Property] Return a list of example value Property instances mirroring the property_list, except that the elements lacking values (e.g. input) are duplicated with values created using the default value of the Property. """ example_properties = [] if property_list: for property in property_list: if property.is_input(): example_properties.append(property.create_value( value=property.get_default_value())) else: example_properties.append(property.copy()) return example_properties