1# -*- coding: utf-8 -*- 2import pytest 3from pybind11_tests import sequences_and_iterators as m 4from pybind11_tests import ConstructorStats 5 6 7def isclose(a, b, rel_tol=1e-05, abs_tol=0.0): 8 """Like math.isclose() from Python 3.5""" 9 return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) 10 11 12def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0): 13 return all( 14 isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list) 15 ) 16 17 18def test_generalized_iterators(): 19 assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero()) == [(1, 2), (3, 4)] 20 assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero()) == [(1, 2)] 21 assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero()) == [] 22 23 assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_keys()) == [1, 3] 24 assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_keys()) == [1] 25 assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_keys()) == [] 26 27 # __next__ must continue to raise StopIteration 28 it = m.IntPairs([(0, 0)]).nonzero() 29 for _ in range(3): 30 with pytest.raises(StopIteration): 31 next(it) 32 33 it = m.IntPairs([(0, 0)]).nonzero_keys() 34 for _ in range(3): 35 with pytest.raises(StopIteration): 36 next(it) 37 38 39def test_sliceable(): 40 sliceable = m.Sliceable(100) 41 assert sliceable[::] == (0, 100, 1) 42 assert sliceable[10::] == (10, 100, 1) 43 assert sliceable[:10:] == (0, 10, 1) 44 assert sliceable[::10] == (0, 100, 10) 45 assert sliceable[-10::] == (90, 100, 1) 46 assert sliceable[:-10:] == (0, 90, 1) 47 assert sliceable[::-10] == (99, -1, -10) 48 assert sliceable[50:60:1] == (50, 60, 1) 49 assert sliceable[50:60:-1] == (50, 60, -1) 50 51 52def test_sequence(): 53 cstats = ConstructorStats.get(m.Sequence) 54 55 s = m.Sequence(5) 56 assert cstats.values() == ["of size", "5"] 57 58 assert "Sequence" in repr(s) 59 assert len(s) == 5 60 assert s[0] == 0 and s[3] == 0 61 assert 12.34 not in s 62 s[0], s[3] = 12.34, 56.78 63 assert 12.34 in s 64 assert isclose(s[0], 12.34) and isclose(s[3], 56.78) 65 66 rev = reversed(s) 67 assert cstats.values() == ["of size", "5"] 68 69 rev2 = s[::-1] 70 assert cstats.values() == ["of size", "5"] 71 72 it = iter(m.Sequence(0)) 73 for _ in range(3): # __next__ must continue to raise StopIteration 74 with pytest.raises(StopIteration): 75 next(it) 76 assert cstats.values() == ["of size", "0"] 77 78 expected = [0, 56.78, 0, 0, 12.34] 79 assert allclose(rev, expected) 80 assert allclose(rev2, expected) 81 assert rev == rev2 82 83 rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) 84 assert cstats.values() == ["of size", "3", "from std::vector"] 85 86 assert allclose(rev, [2, 56.78, 2, 0, 2]) 87 88 assert cstats.alive() == 4 89 del it 90 assert cstats.alive() == 3 91 del s 92 assert cstats.alive() == 2 93 del rev 94 assert cstats.alive() == 1 95 del rev2 96 assert cstats.alive() == 0 97 98 assert cstats.values() == [] 99 assert cstats.default_constructions == 0 100 assert cstats.copy_constructions == 0 101 assert cstats.move_constructions >= 1 102 assert cstats.copy_assignments == 0 103 assert cstats.move_assignments == 0 104 105 106def test_sequence_length(): 107 """#2076: Exception raised by len(arg) should be propagated """ 108 109 class BadLen(RuntimeError): 110 pass 111 112 class SequenceLike: 113 def __getitem__(self, i): 114 return None 115 116 def __len__(self): 117 raise BadLen() 118 119 with pytest.raises(BadLen): 120 m.sequence_length(SequenceLike()) 121 122 assert m.sequence_length([1, 2, 3]) == 3 123 assert m.sequence_length("hello") == 5 124 125 126def test_map_iterator(): 127 sm = m.StringMap({"hi": "bye", "black": "white"}) 128 assert sm["hi"] == "bye" 129 assert len(sm) == 2 130 assert sm["black"] == "white" 131 132 with pytest.raises(KeyError): 133 assert sm["orange"] 134 sm["orange"] = "banana" 135 assert sm["orange"] == "banana" 136 137 expected = {"hi": "bye", "black": "white", "orange": "banana"} 138 for k in sm: 139 assert sm[k] == expected[k] 140 for k, v in sm.items(): 141 assert v == expected[k] 142 143 it = iter(m.StringMap({})) 144 for _ in range(3): # __next__ must continue to raise StopIteration 145 with pytest.raises(StopIteration): 146 next(it) 147 148 149def test_python_iterator_in_cpp(): 150 t = (1, 2, 3) 151 assert m.object_to_list(t) == [1, 2, 3] 152 assert m.object_to_list(iter(t)) == [1, 2, 3] 153 assert m.iterator_to_list(iter(t)) == [1, 2, 3] 154 155 with pytest.raises(TypeError) as excinfo: 156 m.object_to_list(1) 157 assert "object is not iterable" in str(excinfo.value) 158 159 with pytest.raises(TypeError) as excinfo: 160 m.iterator_to_list(1) 161 assert "incompatible function arguments" in str(excinfo.value) 162 163 def bad_next_call(): 164 raise RuntimeError("py::iterator::advance() should propagate errors") 165 166 with pytest.raises(RuntimeError) as excinfo: 167 m.iterator_to_list(iter(bad_next_call, None)) 168 assert str(excinfo.value) == "py::iterator::advance() should propagate errors" 169 170 lst = [1, None, 0, None] 171 assert m.count_none(lst) == 2 172 assert m.find_none(lst) is True 173 assert m.count_nonzeros({"a": 0, "b": 1, "c": 2}) == 2 174 175 r = range(5) 176 assert all(m.tuple_iterator(tuple(r))) 177 assert all(m.list_iterator(list(r))) 178 assert all(m.sequence_iterator(r)) 179 180 181def test_iterator_passthrough(): 182 """#181: iterator passthrough did not compile""" 183 from pybind11_tests.sequences_and_iterators import iterator_passthrough 184 185 values = [3, 5, 7, 9, 11, 13, 15] 186 assert list(iterator_passthrough(iter(values))) == values 187 188 189def test_iterator_rvp(): 190 """#388: Can't make iterators via make_iterator() with different r/v policies """ 191 import pybind11_tests.sequences_and_iterators as m 192 193 assert list(m.make_iterator_1()) == [1, 2, 3] 194 assert list(m.make_iterator_2()) == [1, 2, 3] 195 assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2())) 196