Source code for curry.utility.proptree

'''
Implements a property tree.  Used for metadata.
'''
import collections.abc

__all__ = ['proptree']
NODE_TYPES = {}
DELIMITER = '.'

class _TreeNode(object):
  '''
  Base class of property tree nodes; implements methods.  Derived classes
  should provide __slots__.
  '''
  __slots__ = ()
  def __repr__(self):
    return '[%s]' % ','.join(
        '%s=%s' % (slot, str(getattr(self, slot)))
            for slot in self.__slots__
      )
  def __iter__(self):
    return iter(self.__slots__)
  def __getattr__(self, attr):
    if not attr: return self
    for part in attr.split(DELIMITER):
      self = object.__getattribute__(self, part)
    return self
  def __setattr__(self, attr, value):
    parts = attr.split(DELIMITER)
    self = getattr(self, '.'.join(parts[:-1]))
    object.__setattr__(self, parts[-1], value)
  def __getitem__(self, key):
    try:
      return getattr(self, key)
    except AttributeError:
      raise KeyError(key)
  def __setitem__(self, key, value):
    try:
      setattr(self, key, value)
    except AttributeError:
      raise KeyError(key)
  def __contains__(self, key):
    try:
      getattr(self, key)
    except AttributeError:
      return False
    else:
      return True
  def get(self, key, default=None):
    try:
      return getattr(self, key)
    except AttributeError:
      return default
  def __eq__(self, rhs):
    return all(k==l and self[k]==rhs[k] for k,l in zip(self,rhs))
  def __ne__(self, rhs):
    return not (self == rhs)
  def __lt__(self, rhs): return NotImplemented
  def __gt__(self, rhs): return NotImplemented
  def __le__(self, rhs): return NotImplemented
  def __ge__(self, rhs): return NotImplemented
  def __reduce__(self):
    state = tuple(getattr(self, slot) for slot in self.__slots__)
    return _PropTreeNodeGetter(), (self.__slots__,), state
  def __setstate__(self, state):
    for slot, value in zip(self.__slots__, state):
      setattr(self, slot, value)
  def _keys(self):
    l = []
    for name in self:
      if isinstance(self[name], _TreeNode):
        for tail in self[name]._keys():
          l.append(DELIMITER.join([name, tail]))
      else:
        l.append(name)
    return l
  @property
  def _asdict(self):
    return {attr: getattr(self, attr) for attr in self._keys()}


class _PropTreeNodeGetter(object):
  '''
  Helps make tree nodes pickleable.  __reduce__ must create a pickleable object
  that can be called to produce an example instance whose __class__ attribute
  refers to the intended (and dynamically-generated) class.  Since it appears
  at a module top-level, this class is pickleable.  When called, it produces
  the requested dynamically-generated class.
  '''
  def __call__(self, slots):
    return _gettype(slots)()

def _gettype(slots):
  name = '_'.join(slots)
  if name not in NODE_TYPES:
    NODE_TYPES[name] = type('proptreenode_'+name, (_TreeNode,), {'__slots__': slots})
  return NODE_TYPES[name]

def _buildtree(data):
  # Return non-aggregate data.
  if not isinstance(data, collections.abc.Mapping):
    return data
  # Build a property tree node from mappings.
  slots = tuple(sorted(data.keys()))
  obj = _gettype(slots)()
  for slot in slots:
    setattr(obj, slot, _buildtree(data[slot]))
  return obj

def _error(key, part):
  raise TypeError("key '%s' cannot be created at '%s'" % (key, part))

[docs] def proptree(flat={}): ''' Convert a dict into a property tree. The dictionary may contain dot-delimited keys. It will be converted into a property tree consisting of nested objects whose members correspond to dictionary keys. For example, ``{'a.b':1, 'a.c':2, 'x':3}`` is converted into an object ``t`` such that the expressions ``t.a.b``, ``t.a.c`` and ``t.x`` are bound to 1, 2, and 3, respectively. ''' if not isinstance(flat, collections.abc.Mapping): return flat tree = {} for k,v in flat.items(): t = tree parts = k.split(DELIMITER) for part in parts[:-1]: t = t.setdefault(part, {}) part = parts[-1] try: if part in t: # may raise _error(k, part) t[part] = v except TypeError: _error(k, part) return _buildtree(tree)
EMPTY = proptree()