test_dicttoolz.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. from collections import defaultdict as _defaultdict
  2. from toolz.dicttoolz import (merge, merge_with, valmap, keymap, update_in,
  3. assoc, dissoc, keyfilter, valfilter, itemmap,
  4. itemfilter, assoc_in)
  5. from toolz.utils import raises
  6. from toolz.compatibility import PY3
  7. def inc(x):
  8. return x + 1
  9. def iseven(i):
  10. return i % 2 == 0
  11. class TestDict(object):
  12. """Test typical usage: dict inputs, no factory keyword.
  13. Class attributes:
  14. D: callable that inputs a dict and creates or returns a MutableMapping
  15. kw: kwargs dict to specify "factory" keyword (if applicable)
  16. """
  17. D = dict
  18. kw = {}
  19. def test_merge(self):
  20. D, kw = self.D, self.kw
  21. assert merge(D({1: 1, 2: 2}), D({3: 4}), **kw) == D({1: 1, 2: 2, 3: 4})
  22. def test_merge_iterable_arg(self):
  23. D, kw = self.D, self.kw
  24. assert merge([D({1: 1, 2: 2}), D({3: 4})], **kw) == D({1: 1, 2: 2, 3: 4})
  25. def test_merge_with(self):
  26. D, kw = self.D, self.kw
  27. dicts = D({1: 1, 2: 2}), D({1: 10, 2: 20})
  28. assert merge_with(sum, *dicts, **kw) == D({1: 11, 2: 22})
  29. assert merge_with(tuple, *dicts, **kw) == D({1: (1, 10), 2: (2, 20)})
  30. dicts = D({1: 1, 2: 2, 3: 3}), D({1: 10, 2: 20})
  31. assert merge_with(sum, *dicts, **kw) == D({1: 11, 2: 22, 3: 3})
  32. assert merge_with(tuple, *dicts, **kw) == D({1: (1, 10), 2: (2, 20), 3: (3,)})
  33. assert not merge_with(sum)
  34. def test_merge_with_iterable_arg(self):
  35. D, kw = self.D, self.kw
  36. dicts = D({1: 1, 2: 2}), D({1: 10, 2: 20})
  37. assert merge_with(sum, *dicts, **kw) == D({1: 11, 2: 22})
  38. assert merge_with(sum, dicts, **kw) == D({1: 11, 2: 22})
  39. assert merge_with(sum, iter(dicts), **kw) == D({1: 11, 2: 22})
  40. def test_valmap(self):
  41. D, kw = self.D, self.kw
  42. assert valmap(inc, D({1: 1, 2: 2}), **kw) == D({1: 2, 2: 3})
  43. def test_keymap(self):
  44. D, kw = self.D, self.kw
  45. assert keymap(inc, D({1: 1, 2: 2}), **kw) == D({2: 1, 3: 2})
  46. def test_itemmap(self):
  47. D, kw = self.D, self.kw
  48. assert itemmap(reversed, D({1: 2, 2: 4}), **kw) == D({2: 1, 4: 2})
  49. def test_valfilter(self):
  50. D, kw = self.D, self.kw
  51. assert valfilter(iseven, D({1: 2, 2: 3}), **kw) == D({1: 2})
  52. def test_keyfilter(self):
  53. D, kw = self.D, self.kw
  54. assert keyfilter(iseven, D({1: 2, 2: 3}), **kw) == D({2: 3})
  55. def test_itemfilter(self):
  56. D, kw = self.D, self.kw
  57. assert itemfilter(lambda item: iseven(item[0]), D({1: 2, 2: 3}), **kw) == D({2: 3})
  58. assert itemfilter(lambda item: iseven(item[1]), D({1: 2, 2: 3}), **kw) == D({1: 2})
  59. def test_assoc(self):
  60. D, kw = self.D, self.kw
  61. assert assoc(D({}), "a", 1, **kw) == D({"a": 1})
  62. assert assoc(D({"a": 1}), "a", 3, **kw) == D({"a": 3})
  63. assert assoc(D({"a": 1}), "b", 3, **kw) == D({"a": 1, "b": 3})
  64. # Verify immutability:
  65. d = D({'x': 1})
  66. oldd = d
  67. assoc(d, 'x', 2, **kw)
  68. assert d is oldd
  69. def test_dissoc(self):
  70. D, kw = self.D, self.kw
  71. assert dissoc(D({"a": 1}), "a") == D({})
  72. assert dissoc(D({"a": 1, "b": 2}), "a") == D({"b": 2})
  73. assert dissoc(D({"a": 1, "b": 2}), "b") == D({"a": 1})
  74. assert dissoc(D({"a": 1, "b": 2}), "a", "b") == D({})
  75. assert dissoc(D({"a": 1}), "a") == dissoc(dissoc(D({"a": 1}), "a"), "a")
  76. # Verify immutability:
  77. d = D({'x': 1})
  78. oldd = d
  79. d2 = dissoc(d, 'x')
  80. assert d is oldd
  81. assert d2 is not oldd
  82. def test_assoc_in(self):
  83. D, kw = self.D, self.kw
  84. assert assoc_in(D({"a": 1}), ["a"], 2, **kw) == D({"a": 2})
  85. assert (assoc_in(D({"a": D({"b": 1})}), ["a", "b"], 2, **kw) ==
  86. D({"a": D({"b": 2})}))
  87. assert assoc_in(D({}), ["a", "b"], 1, **kw) == D({"a": D({"b": 1})})
  88. # Verify immutability:
  89. d = D({'x': 1})
  90. oldd = d
  91. d2 = assoc_in(d, ['x'], 2, **kw)
  92. assert d is oldd
  93. assert d2 is not oldd
  94. def test_update_in(self):
  95. D, kw = self.D, self.kw
  96. assert update_in(D({"a": 0}), ["a"], inc, **kw) == D({"a": 1})
  97. assert update_in(D({"a": 0, "b": 1}), ["b"], str, **kw) == D({"a": 0, "b": "1"})
  98. assert (update_in(D({"t": 1, "v": D({"a": 0})}), ["v", "a"], inc, **kw) ==
  99. D({"t": 1, "v": D({"a": 1})}))
  100. # Handle one missing key.
  101. assert update_in(D({}), ["z"], str, None, **kw) == D({"z": "None"})
  102. assert update_in(D({}), ["z"], inc, 0, **kw) == D({"z": 1})
  103. assert update_in(D({}), ["z"], lambda x: x+"ar", default="b", **kw) == D({"z": "bar"})
  104. # Same semantics as Clojure for multiple missing keys, ie. recursively
  105. # create nested empty dictionaries to the depth specified by the
  106. # keys with the innermost value set to f(default).
  107. assert update_in(D({}), [0, 1], inc, default=-1, **kw) == D({0: D({1: 0})})
  108. assert update_in(D({}), [0, 1], str, default=100, **kw) == D({0: D({1: "100"})})
  109. assert (update_in(D({"foo": "bar", 1: 50}), ["d", 1, 0], str, 20, **kw) ==
  110. D({"foo": "bar", 1: 50, "d": D({1: D({0: "20"})})}))
  111. # Verify immutability:
  112. d = D({'x': 1})
  113. oldd = d
  114. update_in(d, ['x'], inc, **kw)
  115. assert d is oldd
  116. def test_factory(self):
  117. D, kw = self.D, self.kw
  118. assert merge(defaultdict(int, D({1: 2})), D({2: 3})) == {1: 2, 2: 3}
  119. assert (merge(defaultdict(int, D({1: 2})), D({2: 3}),
  120. factory=lambda: defaultdict(int)) ==
  121. defaultdict(int, D({1: 2, 2: 3})))
  122. assert not (merge(defaultdict(int, D({1: 2})), D({2: 3}),
  123. factory=lambda: defaultdict(int)) == {1: 2, 2: 3})
  124. assert raises(TypeError, lambda: merge(D({1: 2}), D({2: 3}), factoryy=dict))
  125. class defaultdict(_defaultdict):
  126. def __eq__(self, other):
  127. return (super(defaultdict, self).__eq__(other) and
  128. isinstance(other, _defaultdict) and
  129. self.default_factory == other.default_factory)
  130. class TestDefaultDict(TestDict):
  131. """Test defaultdict as input and factory
  132. Class attributes:
  133. D: callable that inputs a dict and creates or returns a MutableMapping
  134. kw: kwargs dict to specify "factory" keyword (if applicable)
  135. """
  136. @staticmethod
  137. def D(dict_):
  138. return defaultdict(int, dict_)
  139. kw = {'factory': lambda: defaultdict(int)}
  140. class CustomMapping(object):
  141. """Define methods of the MutableMapping protocol required by dicttoolz"""
  142. def __init__(self, *args, **kwargs):
  143. self._d = dict(*args, **kwargs)
  144. def __getitem__(self, key):
  145. return self._d[key]
  146. def __setitem__(self, key, val):
  147. self._d[key] = val
  148. def __delitem__(self, key):
  149. del self._d[key]
  150. def __iter__(self):
  151. return iter(self._d)
  152. def __len__(self):
  153. return len(self._d)
  154. def __contains__(self, key):
  155. return key in self._d
  156. def __eq__(self, other):
  157. return isinstance(other, CustomMapping) and self._d == other._d
  158. def __ne__(self, other):
  159. return not isinstance(other, CustomMapping) or self._d != other._d
  160. def keys(self):
  161. return self._d.keys()
  162. def values(self):
  163. return self._d.values()
  164. def items(self):
  165. return self._d.items()
  166. def update(self, *args, **kwargs):
  167. self._d.update(*args, **kwargs)
  168. # Should we require these to be defined for Python 2?
  169. if not PY3:
  170. def iterkeys(self):
  171. return self._d.iterkeys()
  172. def itervalues(self):
  173. return self._d.itervalues()
  174. def iteritems(self):
  175. return self._d.iteritems()
  176. # Unused methods that are part of the MutableMapping protocol
  177. #def get(self, key, *args):
  178. # return self._d.get(key, *args)
  179. #def pop(self, key, *args):
  180. # return self._d.pop(key, *args)
  181. #def popitem(self, key):
  182. # return self._d.popitem()
  183. #def clear(self):
  184. # self._d.clear()
  185. #def setdefault(self, key, *args):
  186. # return self._d.setdefault(self, key, *args)
  187. class TestCustomMapping(TestDict):
  188. """Test CustomMapping as input and factory
  189. Class attributes:
  190. D: callable that inputs a dict and creates or returns a MutableMapping
  191. kw: kwargs dict to specify "factory" keyword (if applicable)
  192. """
  193. D = CustomMapping
  194. kw = {'factory': lambda: CustomMapping()}