PageRenderTime 26ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/moretools/_multidict.py

https://gitlab.com/zimmermanncode/moretools
Python | 145 lines | 88 code | 32 blank | 25 comment | 25 complexity | af3748002148837cc93a7f1fa858bd79 MD5 | raw file
  1. # python-moretools
  2. #
  3. # many more basic tools for python 2/3
  4. # extending itertools, functools and operator
  5. #
  6. # Copyright (C) 2011-2016 Stefan Zimmermann <zimmermann.code@gmail.com>
  7. #
  8. # python-moretools is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU Lesser General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # python-moretools is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License
  19. # along with python-moretools. If not, see <http://www.gnu.org/licenses/>.
  20. """moretools._multidict
  21. Various container types with a combined interface to multiple `dict`s.
  22. .. moduleauthor:: Stefan Zimmermann <zimmermann.code@gmail.com>
  23. """
  24. __all__ = ['MultiDictType', 'DictSet', 'DictZip', 'DictStruct']
  25. from six import with_metaclass
  26. from ._common import *
  27. class _NoValue:
  28. pass
  29. class _MultiDictBase(object):
  30. def __init__(self, dicts):
  31. self.__dicts__ = list(dicts)
  32. def __len__(self):
  33. return len(set(chain(*(d.keys() for d in self.__dicts__))))
  34. def __getitem__(self, key):
  35. raise NotImplementedError
  36. def __repr__(self):
  37. return '%s(%s)' % (self.__class__.__name__, repr(self.__dicts__))
  38. class MultiDictType(_MultiDictBase):
  39. def keys(self):
  40. for key in set(chain(*(d.keys() for d in self.__dicts__))):
  41. yield key
  42. def values(self):
  43. for key in self.keys():
  44. yield self[key]
  45. def items(self):
  46. for key in self.keys():
  47. yield key, self[key]
  48. def __iter__(self):
  49. return self.keys()
  50. def __contains__(self, key):
  51. return key in self.keys()
  52. class _DictSetMeta(type):
  53. class MultipleKey(LookupError):
  54. pass
  55. MultipleKey.__name__ = 'DictSet.MultipleKey'
  56. class DictSet(with_metaclass(_DictSetMeta, MultiDictType)):
  57. def __getitem__(self, key):
  58. ivalues = (d.get(key, _NoValue) for d in self.__dicts__)
  59. values = [v for v in ivalues if v is not _NoValue]
  60. if not values:
  61. raise KeyError(key)
  62. if len(values) > 1:
  63. raise type(self).MultipleKey(key)
  64. return values[0]
  65. class DictZip(MultiDictType):
  66. def __init__(self, dicts, default_value = _NoValue):
  67. super(type(self), self).__init__(self, dicts)
  68. if default_value is not _NoValue:
  69. self.default_value = default_value
  70. @property
  71. def _default_value(self):
  72. try:
  73. return self.default_value
  74. except AttributeError:
  75. return _NoValue
  76. def __getitem__(self, key):
  77. valuetuple = tuple(
  78. d.get(key, self._default_value) for d in self.__dicts__)
  79. if _NoValue in valuetuple:
  80. raise KeyError(key)
  81. return valuetuple
  82. class DictStruct(MultiDictType, dict):
  83. def __init__(self, name, bases, mapping=None, **items):
  84. self.__name__ = name
  85. self.__bases__ = tuple(bases)
  86. if mapping is None:
  87. dict.__init__(self, **items)
  88. else:
  89. dict.__init__(self, mapping, **items)
  90. def __len__(self):
  91. return len(set(chain(
  92. dict.keys(self), *(d.keys() for d in self.__bases__))))
  93. def __getitem__(self, key):
  94. try:
  95. return dict.__getitem__(self, key)
  96. except KeyError:
  97. for d in self.__bases__:
  98. try:
  99. return d[key]
  100. except KeyError:
  101. pass
  102. raise KeyError(key)
  103. def keys(self):
  104. for key in set(chain(
  105. dict.keys(self), *(d.keys() for d in self.__bases__)
  106. )):
  107. yield key
  108. def __repr__(self):
  109. return '%s(%s, %s, %s)' % (
  110. type(self).__name__, repr(self.__name__), repr(self.__bases__),
  111. dict.__repr__(self))