Wenzel Jakob | 0671002 | 2018-09-01 01:09:16 +0200 | [diff] [blame] | 1 | from __future__ import division |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 2 | import pytest |
| 3 | import sys |
| 4 | |
| 5 | from pybind11_tests import pytypes as m |
| 6 | from pybind11_tests import debug_enabled |
| 7 | |
| 8 | |
| 9 | def test_list(capture, doc): |
| 10 | with capture: |
Jason Rhinelander | 5c7a290 | 2017-10-24 20:39:46 -0300 | [diff] [blame] | 11 | lst = m.get_list() |
kingofpayne | 12e8774 | 2019-08-19 23:00:36 +0200 | [diff] [blame] | 12 | assert lst == ["inserted-0", "overwritten", "inserted-2"] |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 13 | |
Jason Rhinelander | 5c7a290 | 2017-10-24 20:39:46 -0300 | [diff] [blame] | 14 | lst.append("value2") |
| 15 | m.print_list(lst) |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 16 | assert capture.unordered == """ |
| 17 | Entry at position 0: value |
kingofpayne | 12e8774 | 2019-08-19 23:00:36 +0200 | [diff] [blame] | 18 | list item 0: inserted-0 |
| 19 | list item 1: overwritten |
| 20 | list item 2: inserted-2 |
| 21 | list item 3: value2 |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 22 | """ |
| 23 | |
| 24 | assert doc(m.get_list) == "get_list() -> list" |
| 25 | assert doc(m.print_list) == "print_list(arg0: list) -> None" |
| 26 | |
| 27 | |
| 28 | def test_set(capture, doc): |
| 29 | s = m.get_set() |
| 30 | assert s == {"key1", "key2", "key3"} |
| 31 | |
| 32 | with capture: |
| 33 | s.add("key4") |
| 34 | m.print_set(s) |
| 35 | assert capture.unordered == """ |
| 36 | key: key1 |
| 37 | key: key2 |
| 38 | key: key3 |
| 39 | key: key4 |
| 40 | """ |
| 41 | |
Sergei Lebedev | 08b0bda | 2019-08-16 12:32:27 -0700 | [diff] [blame] | 42 | assert not m.set_contains(set([]), 42) |
| 43 | assert m.set_contains({42}, 42) |
| 44 | assert m.set_contains({"foo"}, "foo") |
| 45 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 46 | assert doc(m.get_list) == "get_list() -> list" |
| 47 | assert doc(m.print_list) == "print_list(arg0: list) -> None" |
| 48 | |
| 49 | |
| 50 | def test_dict(capture, doc): |
| 51 | d = m.get_dict() |
| 52 | assert d == {"key": "value"} |
| 53 | |
| 54 | with capture: |
| 55 | d["key2"] = "value2" |
| 56 | m.print_dict(d) |
| 57 | assert capture.unordered == """ |
| 58 | key: key, value=value |
| 59 | key: key2, value=value2 |
| 60 | """ |
| 61 | |
Sergei Lebedev | 08b0bda | 2019-08-16 12:32:27 -0700 | [diff] [blame] | 62 | assert not m.dict_contains({}, 42) |
| 63 | assert m.dict_contains({42: None}, 42) |
| 64 | assert m.dict_contains({"foo": None}, "foo") |
| 65 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 66 | assert doc(m.get_dict) == "get_dict() -> dict" |
| 67 | assert doc(m.print_dict) == "print_dict(arg0: dict) -> None" |
| 68 | |
| 69 | assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} |
| 70 | |
| 71 | |
| 72 | def test_str(doc): |
| 73 | assert m.str_from_string().encode().decode() == "baz" |
| 74 | assert m.str_from_bytes().encode().decode() == "boo" |
| 75 | |
| 76 | assert doc(m.str_from_bytes) == "str_from_bytes() -> str" |
| 77 | |
| 78 | class A(object): |
| 79 | def __str__(self): |
| 80 | return "this is a str" |
| 81 | |
| 82 | def __repr__(self): |
| 83 | return "this is a repr" |
| 84 | |
| 85 | assert m.str_from_object(A()) == "this is a str" |
| 86 | assert m.repr_from_object(A()) == "this is a repr" |
| 87 | |
| 88 | s1, s2 = m.str_format() |
| 89 | assert s1 == "1 + 2 = 3" |
| 90 | assert s1 == s2 |
| 91 | |
| 92 | |
| 93 | def test_bytes(doc): |
| 94 | assert m.bytes_from_string().decode() == "foo" |
| 95 | assert m.bytes_from_str().decode() == "bar" |
| 96 | |
| 97 | assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( |
| 98 | "bytes" if sys.version_info[0] == 3 else "str" |
| 99 | ) |
| 100 | |
| 101 | |
| 102 | def test_capsule(capture): |
| 103 | pytest.gc_collect() |
| 104 | with capture: |
| 105 | a = m.return_capsule_with_destructor() |
| 106 | del a |
| 107 | pytest.gc_collect() |
| 108 | assert capture.unordered == """ |
| 109 | creating capsule |
| 110 | destructing capsule |
| 111 | """ |
| 112 | |
| 113 | with capture: |
| 114 | a = m.return_capsule_with_destructor_2() |
| 115 | del a |
| 116 | pytest.gc_collect() |
| 117 | assert capture.unordered == """ |
| 118 | creating capsule |
| 119 | destructing capsule: 1234 |
| 120 | """ |
| 121 | |
| 122 | with capture: |
| 123 | a = m.return_capsule_with_name_and_destructor() |
| 124 | del a |
| 125 | pytest.gc_collect() |
| 126 | assert capture.unordered == """ |
| 127 | created capsule (1234, 'pointer type description') |
| 128 | destructing capsule (1234, 'pointer type description') |
| 129 | """ |
| 130 | |
| 131 | |
| 132 | def test_accessors(): |
| 133 | class SubTestObject: |
| 134 | attr_obj = 1 |
| 135 | attr_char = 2 |
| 136 | |
| 137 | class TestObject: |
| 138 | basic_attr = 1 |
| 139 | begin_end = [1, 2, 3] |
| 140 | d = {"operator[object]": 1, "operator[char *]": 2} |
| 141 | sub = SubTestObject() |
| 142 | |
| 143 | def func(self, x, *args): |
| 144 | return self.basic_attr + x + sum(args) |
| 145 | |
| 146 | d = m.accessor_api(TestObject()) |
| 147 | assert d["basic_attr"] == 1 |
| 148 | assert d["begin_end"] == [1, 2, 3] |
| 149 | assert d["operator[object]"] == 1 |
| 150 | assert d["operator[char *]"] == 2 |
| 151 | assert d["attr(object)"] == 1 |
| 152 | assert d["attr(char *)"] == 2 |
| 153 | assert d["missing_attr_ptr"] == "raised" |
| 154 | assert d["missing_attr_chain"] == "raised" |
| 155 | assert d["is_none"] is False |
| 156 | assert d["operator()"] == 2 |
| 157 | assert d["operator*"] == 7 |
Dean Moldovan | 2cf87a5 | 2017-09-10 12:21:21 +0200 | [diff] [blame] | 158 | assert d["implicit_list"] == [1, 2, 3] |
| 159 | assert all(x in TestObject.__dict__ for x in d["implicit_dict"]) |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 160 | |
| 161 | assert m.tuple_accessor(tuple()) == (0, 1, 2) |
| 162 | |
| 163 | d = m.accessor_assignment() |
| 164 | assert d["get"] == 0 |
| 165 | assert d["deferred_get"] == 0 |
| 166 | assert d["set"] == 1 |
| 167 | assert d["deferred_set"] == 1 |
| 168 | assert d["var"] == 99 |
| 169 | |
| 170 | |
| 171 | def test_constructors(): |
| 172 | """C++ default and converting constructors are equivalent to type calls in Python""" |
| 173 | types = [str, bool, int, float, tuple, list, dict, set] |
| 174 | expected = {t.__name__: t() for t in types} |
| 175 | assert m.default_constructors() == expected |
| 176 | |
| 177 | data = { |
| 178 | str: 42, |
| 179 | bool: "Not empty", |
| 180 | int: "42", |
| 181 | float: "+1e3", |
| 182 | tuple: range(3), |
| 183 | list: range(3), |
| 184 | dict: [("two", 2), ("one", 1), ("three", 3)], |
| 185 | set: [4, 4, 5, 6, 6, 6], |
| 186 | memoryview: b'abc' |
| 187 | } |
| 188 | inputs = {k.__name__: v for k, v in data.items()} |
| 189 | expected = {k.__name__: k(v) for k, v in data.items()} |
Jason Rhinelander | 373da82 | 2017-08-03 19:27:04 -0400 | [diff] [blame] | 190 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 191 | assert m.converting_constructors(inputs) == expected |
| 192 | assert m.cast_functions(inputs) == expected |
| 193 | |
Jason Rhinelander | 373da82 | 2017-08-03 19:27:04 -0400 | [diff] [blame] | 194 | # Converting constructors and cast functions should just reference rather |
| 195 | # than copy when no conversion is needed: |
| 196 | noconv1 = m.converting_constructors(expected) |
| 197 | for k in noconv1: |
| 198 | assert noconv1[k] is expected[k] |
| 199 | |
| 200 | noconv2 = m.cast_functions(expected) |
| 201 | for k in noconv2: |
| 202 | assert noconv2[k] is expected[k] |
| 203 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 204 | |
| 205 | def test_implicit_casting(): |
| 206 | """Tests implicit casting when assigning or appending to dicts and lists.""" |
| 207 | z = m.get_implicit_casting() |
| 208 | assert z['d'] == { |
| 209 | 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc', |
| 210 | 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3', |
| 211 | 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44 |
| 212 | } |
| 213 | assert z['l'] == [3, 6, 9, 12, 15] |
| 214 | |
| 215 | |
| 216 | def test_print(capture): |
| 217 | with capture: |
| 218 | m.print_function() |
| 219 | assert capture == """ |
| 220 | Hello, World! |
| 221 | 1 2.0 three True -- multiple args |
| 222 | *args-and-a-custom-separator |
| 223 | no new line here -- next print |
| 224 | flush |
| 225 | py::print + str.format = this |
| 226 | """ |
| 227 | assert capture.stderr == "this goes to stderr" |
| 228 | |
| 229 | with pytest.raises(RuntimeError) as excinfo: |
| 230 | m.print_failure() |
| 231 | assert str(excinfo.value) == "make_tuple(): unable to convert " + ( |
| 232 | "argument of type 'UnregisteredType' to Python object" |
| 233 | if debug_enabled else |
| 234 | "arguments to Python object (compile in debug mode for details)" |
| 235 | ) |
Bruce Merry | 37de2da | 2017-08-30 14:22:00 +0200 | [diff] [blame] | 236 | |
| 237 | |
| 238 | def test_hash(): |
| 239 | class Hashable(object): |
| 240 | def __init__(self, value): |
| 241 | self.value = value |
| 242 | |
| 243 | def __hash__(self): |
| 244 | return self.value |
| 245 | |
| 246 | class Unhashable(object): |
| 247 | __hash__ = None |
| 248 | |
| 249 | assert m.hash_function(Hashable(42)) == 42 |
| 250 | with pytest.raises(TypeError): |
| 251 | m.hash_function(Unhashable()) |
Wenzel Jakob | 0671002 | 2018-09-01 01:09:16 +0200 | [diff] [blame] | 252 | |
| 253 | |
| 254 | def test_number_protocol(): |
| 255 | for a, b in [(1, 1), (3, 5)]: |
| 256 | li = [a == b, a != b, a < b, a <= b, a > b, a >= b, a + b, |
| 257 | a - b, a * b, a / b, a | b, a & b, a ^ b, a >> b, a << b] |
| 258 | assert m.test_number_protocol(a, b) == li |
Wenzel Jakob | b4b2292 | 2018-09-01 01:19:16 +0200 | [diff] [blame] | 259 | |
| 260 | |
| 261 | def test_list_slicing(): |
| 262 | li = list(range(100)) |
| 263 | assert li[::2] == m.test_list_slicing(li) |