123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- from pyrsistent._compat import Mapping, Hashable
- import six
- from operator import add
- import pytest
- from pyrsistent import pmap, m, PVector
- import pickle
- def test_instance_of_hashable():
- assert isinstance(m(), Hashable)
- def test_instance_of_map():
- assert isinstance(m(), Mapping)
- def test_literalish_works():
- assert m() is pmap()
- assert m(a=1, b=2) == pmap({'a': 1, 'b': 2})
- def test_empty_initialization():
- map = pmap()
- assert len(map) == 0
- def test_initialization_with_one_element():
- the_map = pmap({'a': 2})
- assert len(the_map) == 1
- assert the_map['a'] == 2
- assert the_map.a == 2
- assert 'a' in the_map
-
- assert the_map is the_map.discard('b')
-
- empty_map = the_map.remove('a')
- assert len(empty_map) == 0
- assert 'a' not in empty_map
- def test_get_non_existing_raises_key_error():
- m1 = m()
- with pytest.raises(KeyError) as error:
- m1['foo']
- assert str(error.value) == "'foo'"
- def test_remove_non_existing_element_raises_key_error():
- m1 = m(a=1)
- with pytest.raises(KeyError) as error:
- m1.remove('b')
- assert str(error.value) == "'b'"
- def test_various_iterations():
- assert set(['a', 'b']) == set(m(a=1, b=2))
- assert ['a', 'b'] == sorted(m(a=1, b=2).keys())
- assert isinstance(m().keys(), PVector)
- assert set([1, 2]) == set(m(a=1, b=2).itervalues())
- assert [1, 2] == sorted(m(a=1, b=2).values())
- assert isinstance(m().values(), PVector)
- assert set([('a', 1), ('b', 2)]) == set(m(a=1, b=2).iteritems())
- assert set([('a', 1), ('b', 2)]) == set(m(a=1, b=2).items())
- assert isinstance(m().items(), PVector)
- def test_initialization_with_two_elements():
- map = pmap({'a': 2, 'b': 3})
- assert len(map) == 2
- assert map['a'] == 2
- assert map['b'] == 3
- map2 = map.remove('a')
- assert 'a' not in map2
- assert map2['b'] == 3
- def test_initialization_with_many_elements():
- init_dict = dict([(str(x), x) for x in range(1700)])
- the_map = pmap(init_dict)
- assert len(the_map) == 1700
- assert the_map['16'] == 16
- assert the_map['1699'] == 1699
- assert the_map.set('256', 256) is the_map
-
- new_map = the_map.remove('1600')
- assert len(new_map) == 1699
- assert '1600' not in new_map
- assert new_map['1601'] == 1601
-
- # Some NOP properties
- assert new_map.discard('18888') is new_map
- assert '19999' not in new_map
- assert new_map['1500'] == 1500
- assert new_map.set('1500', new_map['1500']) is new_map
- def test_access_non_existing_element():
- map1 = pmap()
- assert len(map1) == 0
-
- map2 = map1.set('1', 1)
- assert '1' not in map1
- assert map2['1'] == 1
- assert '2' not in map2
-
- def test_overwrite_existing_element():
- map1 = pmap({'a': 2})
- map2 = map1.set('a', 3)
- assert len(map2) == 1
- assert map2['a'] == 3
- def test_hash():
- x = m(a=1, b=2, c=3)
- y = m(a=1, b=2, c=3)
- assert hash(x) == hash(y)
- def test_same_hash_when_content_the_same_but_underlying_vector_size_differs():
- x = pmap(dict((x, x) for x in range(1000)))
- y = pmap({10: 10, 200: 200, 700: 700})
- for z in x:
- if z not in y:
- x = x.remove(z)
- assert x == y
- assert hash(x) == hash(y)
- class HashabilityControlled(object):
- hashable = True
- def __hash__(self):
- if self.hashable:
- return 4 # Proven random
- raise ValueError("I am not currently hashable.")
- def test_map_does_not_hash_values_on_second_hash_invocation():
- hashable = HashabilityControlled()
- x = pmap(dict(el=hashable))
- hash(x)
- hashable.hashable = False
- hash(x)
- def test_equal():
- x = m(a=1, b=2, c=3)
- y = m(a=1, b=2, c=3)
- assert x == y
- assert not (x != y)
- assert y == x
- assert not (y != x)
- def test_equal_to_dict():
- x = m(a=1, b=2, c=3)
- y = dict(a=1, b=2, c=3)
- assert x == y
- assert not (x != y)
- assert y == x
- assert not (y != x)
- def test_equal_with_different_bucket_sizes():
- x = pmap({'a': 1, 'b': 2}, 50)
- y = pmap({'a': 1, 'b': 2}, 10)
- assert x == y
- assert not (x != y)
- assert y == x
- assert not (y != x)
- def test_equal_with_different_insertion_order():
- x = pmap([(i, i) for i in range(50)], 10)
- y = pmap([(i, i) for i in range(49, -1, -1)], 10)
- assert x == y
- assert not (x != y)
- assert y == x
- assert not (y != x)
- def test_not_equal():
- x = m(a=1, b=2, c=3)
- y = m(a=1, b=2)
- assert x != y
- assert not (x == y)
- assert y != x
- assert not (y == x)
- def test_not_equal_to_dict():
- x = m(a=1, b=2, c=3)
- y = dict(a=1, b=2, d=4)
- assert x != y
- assert not (x == y)
- assert y != x
- assert not (y == x)
- def test_update_with_multiple_arguments():
- # If same value is present in multiple sources, the rightmost is used.
- x = m(a=1, b=2, c=3)
- y = x.update(m(b=4, c=5), {'c': 6})
- assert y == m(a=1, b=4, c=6)
- def test_update_one_argument():
- x = m(a=1)
- assert x.update(m(b=2)) == m(a=1, b=2)
- def test_update_no_arguments():
- x = m(a=1)
- assert x.update() is x
- def test_addition():
- assert m(x=1, y=2) + m(y=3, z=4) == m(x=1, y=3, z=4)
- def test_transform_base_case():
- # Works as set when called with only one key
- x = m(a=1, b=2)
-
- assert x.transform(['a'], 3) == m(a=3, b=2)
- def test_transform_nested_maps():
- x = m(a=1, b=m(c=3, d=m(e=6, f=7)))
-
- assert x.transform(['b', 'd', 'e'], 999) == m(a=1, b=m(c=3, d=m(e=999, f=7)))
- def test_transform_levels_missing():
- x = m(a=1, b=m(c=3))
-
- assert x.transform(['b', 'd', 'e'], 999) == m(a=1, b=m(c=3, d=m(e=999)))
- class HashDummy(object):
- def __hash__(self):
- return 6528039219058920 # Hash of '33'
- def __eq__(self, other):
- return self is other
- def test_hash_collision_is_correctly_resolved():
- dummy1 = HashDummy()
- dummy2 = HashDummy()
- dummy3 = HashDummy()
- dummy4 = HashDummy()
- map = pmap({dummy1: 1, dummy2: 2, dummy3: 3})
- assert map[dummy1] == 1
- assert map[dummy2] == 2
- assert map[dummy3] == 3
- assert dummy4 not in map
-
- keys = set()
- values = set()
- for k, v in map.iteritems():
- keys.add(k)
- values.add(v)
- assert keys == set([dummy1, dummy2, dummy3])
- assert values == set([1, 2, 3])
- map2 = map.set(dummy1, 11)
- assert map2[dummy1] == 11
-
- # Re-use existing structure when inserted element is the same
- assert map2.set(dummy1, 11) is map2
-
- map3 = map.set('a', 22)
- assert map3['a'] == 22
- assert map3[dummy3] == 3
-
- # Remove elements
- map4 = map.discard(dummy2)
- assert len(map4) == 2
- assert map4[dummy1] == 1
- assert dummy2 not in map4
- assert map4[dummy3] == 3
-
- assert map.discard(dummy4) is map
-
- # Empty map handling
- empty_map = map4.remove(dummy1).remove(dummy3)
- assert len(empty_map) == 0
- assert empty_map.discard(dummy1) is empty_map
-
- def test_bitmap_indexed_iteration():
- map = pmap({'a': 2, 'b': 1})
- keys = set()
- values = set()
-
- count = 0
- for k, v in map.iteritems():
- count += 1
- keys.add(k)
- values.add(v)
-
- assert count == 2
- assert keys == set(['a', 'b'])
- assert values == set([2, 1])
- def test_iteration_with_many_elements():
- values = list(range(0, 2000))
- keys = [str(x) for x in values]
- init_dict = dict(zip(keys, values))
-
- hash_dummy1 = HashDummy()
- hash_dummy2 = HashDummy()
-
- # Throw in a couple of hash collision nodes to tests
- # those properly as well
- init_dict[hash_dummy1] = 12345
- init_dict[hash_dummy2] = 54321
- map = pmap(init_dict)
- actual_values = set()
- actual_keys = set()
-
- for k, v in map.iteritems():
- actual_values.add(v)
- actual_keys.add(k)
-
- assert actual_keys == set(keys + [hash_dummy1, hash_dummy2])
- assert actual_values == set(values + [12345, 54321])
- def test_str():
- assert str(pmap({1: 2, 3: 4})) == "pmap({1: 2, 3: 4})"
- def test_empty_truthiness():
- assert m(a=1)
- assert not m()
- def test_update_with():
- assert m(a=1).update_with(add, m(a=2, b=4)) == m(a=3, b=4)
- assert m(a=1).update_with(lambda l, r: l, m(a=2, b=4)) == m(a=1, b=4)
- def map_add(l, r):
- return dict(list(l.items()) + list(r.items()))
- assert m(a={'c': 3}).update_with(map_add, m(a={'d': 4})) == m(a={'c': 3, 'd': 4})
- def test_pickling_empty_map():
- assert pickle.loads(pickle.dumps(m(), -1)) == m()
- def test_pickling_non_empty_map():
- assert pickle.loads(pickle.dumps(m(a=1, b=2), -1)) == m(a=1, b=2)
- def test_set_with_relocation():
- x = pmap({'a':1000}, pre_size=1)
- x = x.set('b', 3000)
- x = x.set('c', 4000)
- x = x.set('d', 5000)
- x = x.set('d', 6000)
- assert len(x) == 4
- assert x == pmap({'a': 1000, 'b': 3000, 'c': 4000, 'd': 6000})
- def test_evolver_simple_update():
- x = m(a=1000, b=2000)
- e = x.evolver()
- e['b'] = 3000
- assert e['b'] == 3000
- assert e.persistent()['b'] == 3000
- assert x['b'] == 2000
- def test_evolver_update_with_relocation():
- x = pmap({'a':1000}, pre_size=1)
- e = x.evolver()
- e['b'] = 3000
- e['c'] = 4000
- e['d'] = 5000
- e['d'] = 6000
- assert len(e) == 4
- assert e.persistent() == pmap({'a': 1000, 'b': 3000, 'c': 4000, 'd': 6000})
- def test_evolver_set_with_reallocation_edge_case():
- # Demonstrates a bug in evolver that also affects updates. Under certain
- # circumstances, the result of `x.update(y)` will **not** have all the
- # keys from `y`.
- foo = object()
- x = pmap({'a': foo}, pre_size=1)
- e = x.evolver()
- e['b'] = 3000
- # Bug is triggered when we do a reallocation and the new value is
- # identical to the old one.
- e['a'] = foo
- y = e.persistent()
- assert 'b' in y
- assert y is e.persistent()
- def test_evolver_remove_element():
- e = m(a=1000, b=2000).evolver()
- assert 'a' in e
- del e['a']
- assert 'a' not in e
- def test_evolver_remove_element_not_present():
- e = m(a=1000, b=2000).evolver()
- with pytest.raises(KeyError) as error:
- del e['c']
- assert str(error.value) == "'c'"
- def test_copy_returns_reference_to_self():
- m1 = m(a=10)
- assert m1.copy() is m1
- def test_dot_access_of_non_existing_element_raises_attribute_error():
- m1 = m(a=10)
- with pytest.raises(AttributeError) as error:
- m1.b
- error_message = str(error.value)
- assert "'b'" in error_message
- assert type(m1).__name__ in error_message
- def test_pmap_unorderable():
- with pytest.raises(TypeError):
- _ = m(a=1) < m(b=2)
- with pytest.raises(TypeError):
- _ = m(a=1) <= m(b=2)
- with pytest.raises(TypeError):
- _ = m(a=1) > m(b=2)
- with pytest.raises(TypeError):
- _ = m(a=1) >= m(b=2)
- def test_supports_weakref():
- import weakref
- weakref.ref(m(a=1))
- def test_iterable():
- """
- PMaps can be created from iterables even though they can't be len() hinted.
- """
- assert pmap(iter([("a", "b")])) == pmap([("a", "b")])
|