class Compound(with_metaclass(_MetaCompound, Mapping, Scalar)): """A mapping container that acts like a scalar value. Compound fields are dictionary-like fields that can assemble a :attr:`u` and :attr:`value` from their children, and can decompose a structured value passed to a :meth:`set` into values for its children. A simple example is a logical calendar date field composed of 3 separate Integer component fields, year, month and day. The Compound can wrap the 3 parts up into a single logical field that handles :class:`datetime.date` values. Set a ``date`` on the logical field and its component fields will be set with year, month and day; alter the int value of the year component field and the logical field updates the ``date`` to match. :class:`Compound` is an abstract class. Subclasses must implement :meth:`compose` and :meth:`explode`. Composites run validation after their children. """ def __compound_init__(cls): """.. TODO:: doc Gist: runs *once* per class, at the time the first element is constructed. You can run it by hand if you want to finalize the construction (see TODO above). Changing class params on instance construction will cause this to run again. """ def compose(self): """Return a text, native tuple built from children's state. :returns: a 2-tuple of text representation, native value. These correspond to the :meth:`~flatland.schema.scalars.Scalar.serialize_element` and :meth:`~flatland.schema.scalars.Scalar.adapt_element` methods of `~flatland.schema.scalars.Scalar` objects. For example, a compound date field may return a '-' delimited string of year, month and day digits and a :class:`datetime.date`. """ raise NotImplementedError() def explode(self, value): """Given a compound value, assign values to children. :param value: a value to be adapted and exploded For example, a compound date field may read attributes from a :class:`datetime.date` value and :meth:`set` them on child fields. The decision to perform type checking on *value* is completely up to you and you may find you want different rules for different compound types. """ raise NotImplementedError() def serialize(self, value): """Not implemented for Compound types.""" raise TypeError("Not implemented for Compound types.") def u(self): uni, value = self.compose() return uni def set_u(self, value): self.explode(value) u = property(u, set_u) del set_u def value(self): uni, value = self.compose() return value def set_value(self, value): self.explode(value) value = property(value, set_value) del set_value def set(self, value): self.raw = value try: res = self.explode(value) # TODO: historically explode() did not need to have a return value # but it would be nice to return it form set() as below. res = True if res is None else res # compat element_set.send(self, adapted=res) return res except (SystemExit, KeyboardInterrupt, NotImplementedError): raise except Exception: # not wild about quashing here, but set() doesn't allow # adaptation exceptions to bubble up. element_set.send(self, adapted=False) return False def _set_flat(self, pairs, sep): Mapping._set_flat(self, pairs, sep) def __repr__(self): try: return Scalar.__repr__(self) except Exception as exc: return '<%s %r; value raised %s>' % (type(self).__name__, self.name, type(exc).__name__) @property def is_empty(self): """True if all subfields are empty.""" return reduce(operator.and_, (c.is_empty for c in self.children))
class Form(with_metaclass(_MetaSchema, Dict)): """An alias for Schema, for older flatland version compatibility."""
class Schema(with_metaclass(_MetaSchema, Dict)): """A declarative collection of named elements.
class SparseSchema(with_metaclass(_MetaSchema, SparseDict)): """A sparse variant of `~flatland.schema.declarative.Schema`.