core.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- coding: utf-8 -*-
  2. '''core stuf.'''
  3. from itertools import chain
  4. from collections import Mapping, defaultdict, namedtuple
  5. from ._core import basewrite, writestuf, writewrapstuf, wraps, wrapstuf, asdict
  6. from .deep import getcls
  7. from .iterable import exhaustcall
  8. from .desc import lazy_class, lazy
  9. from .collects import ChainMap, Counter, OrderedDict
  10. __all__ = 'defaultstuf fixedstuf frozenstuf orderedstuf stuf'.split()
  11. class chainstuf(basewrite, ChainMap):
  12. '''stuf chained together.'''
  13. def __init__(self, *args):
  14. super(chainstuf, self).__init__(*args)
  15. maps = self.maps
  16. for idx, item in enumerate(maps):
  17. maps[idx] = stuf(item)
  18. def __reduce__(self):
  19. return (getcls(self), tuple(self.maps))
  20. @lazy_class
  21. def _classkeys(self):
  22. # protected keywords
  23. return frozenset(chain(super(chainstuf, self)._classkeys, ['maps']))
  24. copy = ChainMap.copy
  25. update = ChainMap.update
  26. class countstuf(basewrite, Counter):
  27. '''stuf that counts.'''
  28. class defaultstuf(writestuf, defaultdict):
  29. '''
  30. Dictionary with attribute-style access and a factory function to provide a
  31. default value for keys with no value.
  32. '''
  33. __slots__ = []
  34. _map = defaultdict
  35. def __init__(self, default, *args, **kw):
  36. '''
  37. :argument default: function that can provide default values
  38. :param *args: iterable of keys/value pairs
  39. :param **kw: keyword arguments
  40. '''
  41. defaultdict.__init__(self, default)
  42. writestuf.update(self, *args, **kw)
  43. def _build(self, iterable):
  44. # add class to handle potential nested objects of the same class
  45. try:
  46. kind = self._map
  47. kw = kind(self.default_factory)
  48. # extract appropriate key-values from sequence
  49. exhaustcall(kw.update, iterable)
  50. except (ValueError, TypeError):
  51. kw.update(kind(self.default_factory, iterable))
  52. return kw
  53. def _new(self, iterable):
  54. return getcls(self)(self.default_factory, self._build(iterable))
  55. class fixedstuf(writewrapstuf):
  56. '''
  57. Dictionary with attribute-style access where mutability is restricted to
  58. initial keys.
  59. '''
  60. def __setitem__(self, key, value):
  61. # only access initial keys
  62. if key in self.allowed:
  63. super(fixedstuf, self).__setitem__(key, value)
  64. else:
  65. raise KeyError('key "{0}" not allowed'.format(key))
  66. def _prepop(self, *args, **kw):
  67. iterable = super(fixedstuf, self)._prepop(*args, **kw)
  68. self.allowed = frozenset(iterable)
  69. return iterable
  70. def clear(self):
  71. wraps(self).clear()
  72. def popitem(self):
  73. raise AttributeError()
  74. def pop(self, key, default=None):
  75. raise AttributeError()
  76. class frozenstuf(wrapstuf, Mapping):
  77. '''Immutable dictionary with attribute-style access.'''
  78. __slots__ = ['_wrapped']
  79. def __getitem__(self, key):
  80. try:
  81. return getattr(wraps(self), key)
  82. except AttributeError:
  83. raise KeyError('key {0} not found'.format(key))
  84. def __iter__(self):
  85. return iter(asdict(self)())
  86. def __len__(self):
  87. return len(asdict(self)())
  88. def __reduce__(self):
  89. return (getcls(self), (asdict(self)().copy(),))
  90. @classmethod
  91. def _mapping(self, mapping):
  92. return namedtuple('frozenstuf', iter(mapping))(**mapping)
  93. class orderedstuf(writewrapstuf):
  94. '''Dictionary with dot attributes that remember insertion order.'''
  95. _mapping = OrderedDict
  96. @lazy
  97. def __reversed__(self):
  98. return wraps(self).__reversed__
  99. @classmethod
  100. def fromkeys(cls, keys, value=None):
  101. return cls(cls._mapping.fromkeys(keys, value))
  102. class stuf(writestuf, dict):
  103. '''Dictionary with attribute-style access.'''
  104. __slots__ = []
  105. __init__ = writestuf.update